| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| node-npmtest-altair.io/ | 100% | (153 / 153) | 100% | (126 / 126) | 100% | (28 / 28) | 100% | (153 / 153) | |
| node-npmtest-altair.io/node_modules/altair.io/bin/ | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) | |
| node-npmtest-altair.io/node_modules/altair.io/core/ | 2.22% | (1 / 45) | 0% | (0 / 10) | 0% | (0 / 5) | 2.22% | (1 / 45) | |
| node-npmtest-altair.io/node_modules/altair.io/core/lib/altair/ | 1.42% | (5 / 353) | 0% | (0 / 210) | 0% | (0 / 82) | 1.42% | (5 / 353) | |
| node-npmtest-altair.io/node_modules/altair.io/core/lib/apollo/ | 2.66% | (5 / 188) | 0% | (0 / 137) | 0% | (0 / 63) | 2.66% | (5 / 188) | |
| node-npmtest-altair.io/node_modules/altair.io/core/lib/doh/ | 1.02% | (16 / 1562) | 0% | (0 / 784) | 0.37% | (1 / 267) | 1.05% | (16 / 1531) | |
| node-npmtest-altair.io/node_modules/altair.io/core/lib/dojo/ | 8.21% | (401 / 4886) | 5.63% | (203 / 3608) | 5.18% | (42 / 811) | 8.38% | (400 / 4771) | |
| node-npmtest-altair.io/node_modules/altair.io/core/lib/dojo/_base/ | 58.33% | (14 / 24) | 25% | (1 / 4) | 40% | (2 / 5) | 58.33% | (14 / 24) | |
| node-npmtest-altair.io/node_modules/altair.io/core/lib/lodash/ | 30.29% | (597 / 1971) | 9.52% | (162 / 1701) | 11.48% | (31 / 270) | 30.25% | (595 / 1967) | |
| node-npmtest-altair.io/node_modules/altair.io/locales/ | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (0 / 0) |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| example.js | 100% | (83 / 83) | 100% | (73 / 73) | 100% | (12 / 12) | 100% | (83 / 83) | |
| lib.npmtest_altair.io.js | 100% | (16 / 16) | 100% | (14 / 14) | 100% | (3 / 3) | 100% | (16 / 16) | |
| test.js | 100% | (54 / 54) | 100% | (39 / 39) | 100% | (13 / 13) | 100% | (54 / 54) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | 2 2 2 2 2 2 2 1 2 2 2 2 1 2 2 2 2 2 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 2 3 3 3 3 1 3 3 3 1 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 | /*
example.js
quickstart example
instruction
1. save this script as example.js
2. run the shell command:
$ npm install npmtest-altair.io && PORT=8081 node example.js
3. play with the browser-demo on http://127.0.0.1:8081
*/
/* istanbul instrument in package npmtest_altair_io */
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 96,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
(function () {
'use strict';
var local;
// run shared js-env code - pre-init
(function () {
// init local
local = {};
// init modeJs
local.modeJs = (function () {
try {
return typeof navigator.userAgent === 'string' &&
typeof document.querySelector('body') === 'object' &&
typeof XMLHttpRequest.prototype.open === 'function' &&
'browser';
} catch (errorCaughtBrowser) {
return module.exports &&
typeof process.versions.node === 'string' &&
typeof require('http').createServer === 'function' &&
'node';
}
}());
// init global
local.global = local.modeJs === 'browser'
? window
: global;
// init utility2_rollup
local = local.global.utility2_rollup || (local.modeJs === 'browser'
? local.global.utility2_npmtest_altair_io
: global.utility2_moduleExports);
// export local
local.global.local = local;
}());
switch (local.modeJs) {
// post-init
// run browser js-env code - post-init
/* istanbul ignore next */
case 'browser':
local.testRunBrowser = function (event) {
Eif (!event || (event &&
event.currentTarget &&
event.currentTarget.className &&
event.currentTarget.className.includes &&
event.currentTarget.className.includes('onreset'))) {
// reset output
Array.from(
document.querySelectorAll('body > .resettable')
).forEach(function (element) {
switch (element.tagName) {
case 'INPUT':
case 'TEXTAREA':
element.value = '';
break;
default:
element.textContent = '';
}
});
}
switch (event && event.currentTarget && event.currentTarget.id) {
case 'testRunButton1':
// show tests
Eif (document.querySelector('#testReportDiv1').style.display === 'none') {
document.querySelector('#testReportDiv1').style.display = 'block';
document.querySelector('#testRunButton1').textContent =
'hide internal test';
local.modeTest = true;
local.testRunDefault(local);
// hide tests
} else {
document.querySelector('#testReportDiv1').style.display = 'none';
document.querySelector('#testRunButton1').textContent = 'run internal test';
}
break;
// custom-case
default:
break;
}
Iif (document.querySelector('#inputTextareaEval1') && (!event || (event &&
event.currentTarget &&
event.currentTarget.className &&
event.currentTarget.className.includes &&
event.currentTarget.className.includes('oneval')))) {
// try to eval input-code
try {
/*jslint evil: true*/
eval(document.querySelector('#inputTextareaEval1').value);
} catch (errorCaught) {
console.error(errorCaught);
}
}
};
// log stderr and stdout to #outputTextareaStdout1
['error', 'log'].forEach(function (key) {
console[key + '_original'] = console[key];
console[key] = function () {
var element;
console[key + '_original'].apply(console, arguments);
element = document.querySelector('#outputTextareaStdout1');
Iif (!element) {
return;
}
// append text to #outputTextareaStdout1
element.value += Array.from(arguments).map(function (arg) {
return typeof arg === 'string'
? arg
: JSON.stringify(arg, null, 4);
}).join(' ') + '\n';
// scroll textarea to bottom
element.scrollTop = element.scrollHeight;
};
});
// init event-handling
['change', 'click', 'keyup'].forEach(function (event) {
Array.from(document.querySelectorAll('.on' + event)).forEach(function (element) {
element.addEventListener(event, local.testRunBrowser);
});
});
// run tests
local.testRunBrowser();
break;
// run node js-env code - post-init
/* istanbul ignore next */
case 'node':
// export local
module.exports = local;
// require modules
local.fs = require('fs');
local.http = require('http');
local.url = require('url');
// init assets
local.assetsDict = local.assetsDict || {};
/* jslint-ignore-begin */
local.assetsDict['/assets.index.template.html'] = '\
<!doctype html>\n\
<html lang="en">\n\
<head>\n\
<meta charset="UTF-8">\n\
<meta name="viewport" content="width=device-width, initial-scale=1">\n\
<title>{{env.npm_package_name}} (v{{env.npm_package_version}})</title>\n\
<style>\n\
/*csslint\n\
box-sizing: false,\n\
universal-selector: false\n\
*/\n\
* {\n\
box-sizing: border-box;\n\
}\n\
body {\n\
background: #dde;\n\
font-family: Arial, Helvetica, sans-serif;\n\
margin: 2rem;\n\
}\n\
body > * {\n\
margin-bottom: 1rem;\n\
}\n\
.utility2FooterDiv {\n\
margin-top: 20px;\n\
text-align: center;\n\
}\n\
</style>\n\
<style>\n\
/*csslint\n\
*/\n\
textarea {\n\
font-family: monospace;\n\
height: 10rem;\n\
width: 100%;\n\
}\n\
textarea[readonly] {\n\
background: #ddd;\n\
}\n\
</style>\n\
</head>\n\
<body>\n\
<!-- utility2-comment\n\
<div id="ajaxProgressDiv1" style="background: #d00; height: 2px; left: 0; margin: 0; padding: 0; position: fixed; top: 0; transition: background 0.5s, width 1.5s; width: 25%;"></div>\n\
utility2-comment -->\n\
<h1>\n\
<!-- utility2-comment\n\
<a\n\
{{#if env.npm_package_homepage}}\n\
href="{{env.npm_package_homepage}}"\n\
{{/if env.npm_package_homepage}}\n\
target="_blank"\n\
>\n\
utility2-comment -->\n\
{{env.npm_package_name}} (v{{env.npm_package_version}})\n\
<!-- utility2-comment\n\
</a>\n\
utility2-comment -->\n\
</h1>\n\
<h3>{{env.npm_package_description}}</h3>\n\
<!-- utility2-comment\n\
<h4><a download href="assets.app.js">download standalone app</a></h4>\n\
<button class="onclick onreset" id="testRunButton1">run internal test</button><br>\n\
<div id="testReportDiv1" style="display: none;"></div>\n\
utility2-comment -->\n\
\n\
\n\
\n\
<label>stderr and stdout</label>\n\
<textarea class="resettable" id="outputTextareaStdout1" readonly></textarea>\n\
<!-- utility2-comment\n\
{{#if isRollup}}\n\
<script src="assets.app.js"></script>\n\
{{#unless isRollup}}\n\
utility2-comment -->\n\
<script src="assets.utility2.rollup.js"></script>\n\
<script src="jsonp.utility2._stateInit?callback=window.utility2._stateInit"></script>\n\
<script src="assets.npmtest_altair_io.rollup.js"></script>\n\
<script src="assets.example.js"></script>\n\
<script src="assets.test.js"></script>\n\
<!-- utility2-comment\n\
{{/if isRollup}}\n\
utility2-comment -->\n\
<div class="utility2FooterDiv">\n\
[ this app was created with\n\
<a href="https://github.com/kaizhu256/node-utility2" target="_blank">utility2</a>\n\
]\n\
</div>\n\
</body>\n\
</html>\n\
';
/* jslint-ignore-end */
Iif (local.templateRender) {
local.assetsDict['/'] = local.templateRender(
local.assetsDict['/assets.index.template.html'],
{
env: local.objectSetDefault(local.env, {
npm_package_description: 'the greatest app in the world!',
npm_package_name: 'my-app',
npm_package_nameAlias: 'my_app',
npm_package_version: '0.0.1'
})
}
);
} else {
local.assetsDict['/'] = local.assetsDict['/assets.index.template.html']
.replace((/\{\{env\.(\w+?)\}\}/g), function (match0, match1) {
// jslint-hack
String(match0);
switch (match1) {
case 'npm_package_description':
return 'the greatest app in the world!';
case 'npm_package_name':
return 'my-app';
case 'npm_package_nameAlias':
return 'my_app';
case 'npm_package_version':
return '0.0.1';
}
});
}
// run the cli
Eif (local.global.utility2_rollup || module !== require.main) {
break;
}
local.assetsDict['/assets.example.js'] =
local.assetsDict['/assets.example.js'] ||
local.fs.readFileSync(__filename, 'utf8');
// bug-workaround - long $npm_package_buildCustomOrg
/* jslint-ignore-begin */
local.assetsDict['/assets.npmtest_altair_io.rollup.js'] =
local.assetsDict['/assets.npmtest_altair_io.rollup.js'] ||
local.fs.readFileSync(
local.npmtest_altair_io.__dirname + '/lib.npmtest_altair_io.js',
'utf8'
).replace((/^#!/), '//');
/* jslint-ignore-end */
local.assetsDict['/favicon.ico'] = local.assetsDict['/favicon.ico'] || '';
// if $npm_config_timeout_exit exists,
// then exit this process after $npm_config_timeout_exit ms
if (Number(process.env.npm_config_timeout_exit)) {
setTimeout(process.exit, Number(process.env.npm_config_timeout_exit));
}
// start server
if (local.global.utility2_serverHttp1) {
break;
}
process.env.PORT = process.env.PORT || '8081';
console.error('server starting on port ' + process.env.PORT);
local.http.createServer(function (request, response) {
request.urlParsed = local.url.parse(request.url);
if (local.assetsDict[request.urlParsed.pathname] !== undefined) {
response.end(local.assetsDict[request.urlParsed.pathname]);
return;
}
response.statusCode = 404;
response.end();
}).listen(process.env.PORT);
break;
}
}());
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 2 2 2 2 2 2 2 1 2 2 2 2 1 1 1 1 | /* istanbul instrument in package npmtest_altair_io */
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 96,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
(function () {
'use strict';
var local;
// run shared js-env code - pre-init
(function () {
// init local
local = {};
// init modeJs
local.modeJs = (function () {
try {
return typeof navigator.userAgent === 'string' &&
typeof document.querySelector('body') === 'object' &&
typeof XMLHttpRequest.prototype.open === 'function' &&
'browser';
} catch (errorCaughtBrowser) {
return module.exports &&
typeof process.versions.node === 'string' &&
typeof require('http').createServer === 'function' &&
'node';
}
}());
// init global
local.global = local.modeJs === 'browser'
? window
: global;
// init utility2_rollup
local = local.global.utility2_rollup || local;
// init lib
local.local = local.npmtest_altair_io = local;
// init exports
if (local.modeJs === 'browser') {
local.global.utility2_npmtest_altair_io = local;
} else {
module.exports = local;
module.exports.__dirname = __dirname;
module.exports.module = module;
}
}());
}());
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | 2 2 2 2 2 2 2 1 2 2 1 1 1 1 2 2 2 2 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 2 2 1 2 2 1 2 2 1 1 1 1 1 | /* istanbul instrument in package npmtest_altair_io */
/*jslint
bitwise: true,
browser: true,
maxerr: 8,
maxlen: 96,
node: true,
nomen: true,
regexp: true,
stupid: true
*/
(function () {
'use strict';
var local;
// run shared js-env code - pre-init
(function () {
// init local
local = {};
// init modeJs
local.modeJs = (function () {
try {
return typeof navigator.userAgent === 'string' &&
typeof document.querySelector('body') === 'object' &&
typeof XMLHttpRequest.prototype.open === 'function' &&
'browser';
} catch (errorCaughtBrowser) {
return module.exports &&
typeof process.versions.node === 'string' &&
typeof require('http').createServer === 'function' &&
'node';
}
}());
// init global
local.global = local.modeJs === 'browser'
? window
: global;
switch (local.modeJs) {
// re-init local from window.local
case 'browser':
local = local.global.utility2.objectSetDefault(
local.global.utility2_rollup || local.global.local,
local.global.utility2
);
break;
// re-init local from example.js
case 'node':
local = (local.global.utility2_rollup || require('utility2'))
.requireReadme();
break;
}
// export local
local.global.local = local;
}());
// run shared js-env code - function
(function () {
return;
}());
switch (local.modeJs) {
// run browser js-env code - function
case 'browser':
break;
// run node js-env code - function
case 'node':
break;
}
// run shared js-env code - post-init
(function () {
return;
}());
switch (local.modeJs) {
// run browser js-env code - post-init
case 'browser':
local.testCase_browser_nullCase = local.testCase_browser_nullCase || function (
options,
onError
) {
/*
* this function will test browsers's null-case handling-behavior-behavior
*/
onError(null, options);
};
// run tests
local.nop(local.modeTest &&
document.querySelector('#testRunButton1') &&
document.querySelector('#testRunButton1').click());
break;
// run node js-env code - post-init
/* istanbul ignore next */
case 'node':
local.testCase_buildApidoc_default = local.testCase_buildApidoc_default || function (
options,
onError
) {
/*
* this function will test buildApidoc's default handling-behavior-behavior
*/
options = { modulePathList: module.paths };
local.buildApidoc(options, onError);
};
local.testCase_buildApp_default = local.testCase_buildApp_default || function (
options,
onError
) {
/*
* this function will test buildApp's default handling-behavior-behavior
*/
local.testCase_buildReadme_default(options, local.onErrorThrow);
local.testCase_buildLib_default(options, local.onErrorThrow);
local.testCase_buildTest_default(options, local.onErrorThrow);
local.testCase_buildCustomOrg_default(options, local.onErrorThrow);
options = [];
local.buildApp(options, onError);
};
local.testCase_buildCustomOrg_default = local.testCase_buildCustomOrg_default ||
function (options, onError) {
/*
* this function will test buildCustomOrg's default handling-behavior
*/
options = {};
local.buildCustomOrg(options, onError);
};
local.testCase_buildLib_default = local.testCase_buildLib_default || function (
options,
onError
) {
/*
* this function will test buildLib's default handling-behavior
*/
options = {};
local.buildLib(options, onError);
};
local.testCase_buildReadme_default = local.testCase_buildReadme_default || function (
options,
onError
) {
/*
* this function will test buildReadme's default handling-behavior-behavior
*/
options = {};
local.buildReadme(options, onError);
};
local.testCase_buildTest_default = local.testCase_buildTest_default || function (
options,
onError
) {
/*
* this function will test buildTest's default handling-behavior
*/
options = {};
local.buildTest(options, onError);
};
local.testCase_webpage_default = local.testCase_webpage_default || function (
options,
onError
) {
/*
* this function will test webpage's default handling-behavior
*/
options = { modeCoverageMerge: true, url: local.serverLocalHost + '?modeTest=1' };
local.browserTest(options, onError);
};
// run test-server
local.testRunServer(local);
break;
}
}());
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| altair.js | 100% | (1 / 1) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (1 / 1) |
| 1 2 3 | 2 | //#!/usr/bin/env node
require('../altair.js');
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| bootstrap.js | 2.22% | (1 / 45) | 0% | (0 / 10) | 0% | (0 / 5) | 2.22% | (1 / 45) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | 2 | /**
* Bootstrap Altair instances based on a config
*/
require(['altair/Altair',
'require',
'altair/cartridges/Foundry',
'altair/facades/mixin',
'altair/facades/home',
'altair/plugins/node!debug',
'altair/plugins/node!path',
'altair/plugins/node!fs',
'altair/plugins/node!module',
'altair/plugins/node!config-extend',
'lodash',
'altair/plugins/config!core/config/altair.json?env=' + global.env],
function (Altair, require, Foundry, mixin, home, debugUtil, path, fs, Module, extend, _, config) {
/**
* Simple debug logging
*/
debugUtil.enable(config.debug);
debug = debugUtil('altair:Altair');
require.log = debug; //overrides the require.log used by dojo to provide much better high level reporting
/**
* Make sure our CWD is set
*/
if(global.cwd !== process.cwd()) {
process.chdir(global.cwd);
}
/**
* NPM has zero dependency injection so it's easier to create a central place for altair to manage
* all node dependencies than it is to configure npm (at all). This is where all the dependencies
* for altair modules/themes/widgets/sites will be installed.
*/
var homePath = path.join(home(), '.altair'),
homeConfigPath = path.join(homePath, 'altair.json'),
homePackagePath = path.join(homePath, 'package.json'),
appConfigPath = path.join(process.cwd(), 'altair.json');
//does our run dir exist? move this to better installer
try {
fs.statSync(homePath);
} catch (e) {
debug('altair first run, creating ' + homePath);
//create home
fs.mkdirSync(homePath);
fs.writeFileSync(homeConfigPath, JSON.stringify({
description: 'See https://github.com/liquidg3/altair/blob/master/docs/config.md for help on configuring altair.',
'default': {
paths: {
core: 'core'
}
}
}, null, 4));
fs.writeFileSync(homePackagePath, JSON.stringify({
name: 'altair-global',
description: 'Placeholder altair config to hold dependencies of all installed modules.'
}, null, 4));
}
/**
* Mixin config from app/config/altair.json if there is one
*/
require([
'altair/plugins/config!' + appConfigPath + '?env=' + global.env,
'altair/plugins/config!' + homeConfigPath + '?env=' + global.env
], function (appConfig, homeConfig) {
var paths = [],
altair,
app,
foundry;
config.paths.home = homePath; //always have a home path
//inform us about our environment
debug('current environment is "' + global.env + '".');
//mixin configs, cwd config wins!
if(appConfig) {
//if the cwdConfig exists, it is our new "home" folder and everything will run/install from there
debug('app detected - loading config @ ' + appConfigPath);
extend(config, appConfig);
//our new home (also make sure it's in the paths)
app = process.cwd();
config.paths.app = app;
} else {
debug('loading config @ ' + homeConfigPath);
config = mixin(config, homeConfig);
}
/**
* Lets you configure how much error reporting to do.
*
* @type {number}
*/
Error.stackTraceLimit = config.stackTraceLimit || Infinity;
//set debug config again
debugUtil.names = [];
debugUtil.skips = [];
debugUtil.enable(config.debug);
/**
* Make sure npm can look at our current app directory, fallback to home. But NEVER both.
*
* @type {string}
*/
process.env['NODE_PATH'] += ":" + path.join(homePath, 'node_modules');
if(app) { //app (which is cwd if altair.json exists) is a valid lookup spot
process.env['NODE_PATH'] += ":" + path.join(app, 'node_modules');
}
Module._initPaths(); // terrible
/**
* Bring in the packages from the config, this should point to at least app and core. Even though core is not
* needed, this array is also used to build our lookup paths in altair. Altair only needs their names since
* dojo's define() and require() can map it to their paths.
*/
require({
paths: config.paths
});
//cartridges are given a key in the config so they can be overridden easier.
config.cartridges = _.toArray(config.cartridges);
//paths by name for altair
Object.keys(config.paths).forEach(function (name) {
paths.push(name);
});
/**
* Startup the cartridge factory and create the cartridges, then add
* them to altair.
*/
altair = new Altair({ paths: paths, safeMode: global.safe, home: homePath });
foundry = new Foundry(altair);
if (altair.safeMode) {
debug('-- starting altair in safe mode --');
}
debug('creating cartridge foundry. adding ' + config.cartridges.length + ' cartridges.');
foundry.build(config.cartridges).then(function (cartridges) {
debug('cartridges created. adding to altair for startup.');
/**
* Add cartridges
*/
return altair.addCartridges(cartridges).then(function () {
debug('cartridges started.');
});
}).otherwise(debug);
});
});
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| Altair.js | 1.72% | (1 / 58) | 0% | (0 / 30) | 0% | (0 / 20) | 1.72% | (1 / 58) | |
| Deferred.js | 0.73% | (1 / 137) | 0% | (0 / 88) | 0% | (0 / 23) | 0.73% | (1 / 137) | |
| Lifecycle.js | 2.86% | (1 / 35) | 0% | (0 / 24) | 0% | (0 / 10) | 2.86% | (1 / 35) | |
| StateMachine.js | 1.32% | (1 / 76) | 0% | (0 / 44) | 0% | (0 / 18) | 1.32% | (1 / 76) | |
| TestRunner.js | 2.13% | (1 / 47) | 0% | (0 / 24) | 0% | (0 / 11) | 2.13% | (1 / 47) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | 2 | /**
* Well, this is it... Altair in its entirety. The whole platform is simply a cartridge loader. These cartridges are
* responsible for enhancing the environment in various ways. It should be really easy to augment the platform at a very
* low level this way. Chances are that if you need to add new functionality into the platform you should be doing it
* as a module. The only things that should be cartridges are components that need to exist before the module system
* is ready. Currently, this is things like Nexus (Dependency Injection), Apollo (ORM), Cache, Database, and a few others.
* See core/config/altair.json to see the current config.
*/
define(['altair/facades/declare',
'altair/Deferred',
'lodash',
'altair/facades/hitch',
'altair/facades/home',
'altair/facades/all',
'altair/facades/when',
'altair/plugins/node!path',
'altair/events/Emitter'
],
function (declare, Deferred, _, hitch, home, all, when, path, Emitter) {
//helper for us elsewhere *find better place*
_.isObjectLiteral = function (_obj) {
var _test = _obj;
return ( typeof _obj !== 'object' || _obj === null ?
false :
(
(function () {
while (!false) {
if ( Object.getPrototypeOf( _test = Object.getPrototypeOf(_test) ) === null) {
break;
}
}
return Object.getPrototypeOf(_obj) === _test;
})()
)
);
};
"use strict";
return declare([Emitter], {
_cartridges: null,
env: 'dev',
paths: null,
safeMode: false,
home: '',
constructor: function (options) {
var _options = options || {};
this.paths = _options.paths || [];
this.env = _options.env || 'dev';
this.safeMode = _.has(_options, 'safeMode') ? _options.safeMode : false;
this.home = _.has(_options, 'home') ? _options.home : path.join(home(), '.altair');
this._cartridges = {};
},
/**
* Resolve a path against the last path in my paths. home by default
*
* @param p String
* @returns {String}
*/
resolvePath: function (p) {
var resolved = p;
//if it starts with ./, then assume it's from the current working directory
if (p[0] === '.') {
resolved = path.join(process.cwd(), p);
}
//relative paths (no /) all look from app or home
else if(p[0] != path.sep) {
resolved = path.join(require.toUrl(this.paths[this.paths.length - 1]), p);
}
return resolved;
},
/**
* Add an un-started cartridge and I'll add it to the system and start it up. optionally i will execute it.
*
* @param cartridge
* @param execute should I execute the cartridge after startup??
* @returns {altair.Promise}
*/
addCartridge: function (cartidge, execute) {
this._cartridges[cartidge.name] = cartidge;
return cartidge.startup().then(function (cartridge) {
if (execute !== false) {
return cartridge.execute();
} else {
return cartridge;
}
});
},
/**
* Removes a cartridge, but tears it down first.
*
* @param key
* @returns {altair.Promise}
*/
removeCartridge: function (name) {
var def = this.cartridge(name).teardown();
delete this._cartridges[name];
return def;
},
/**
* All the cartridges by name.
*
* @returns {object}
*/
cartridges: function () {
return this._cartridges;
},
/**
* Get a cartridge by it's key
*
* @param key
* @returns {*|null}
*/
cartridge: function (name) {
return this._cartridges[name] || null;
},
/**
* Is this cartridge loaded?
*
* @param name
* @returns {boolean}
*/
hasCartridge: function (name) {
return !!this._cartridges[name];
},
/**
* Quick check if all the cartridges are loaded. If any single one is missing,
* it returns false.
*
* @param namees
* @returns {boolean}
*/
hasCartridges: function (namees) {
var i;
for (i = 0; i < namees.length; i++) {
if (!this.hasCartridge(namees[i])) {
return false;
}
}
return true;
},
/**
* Add a collection of cartridges. Each cartridge will be started up AFTER the one before it. This is to ensure
* dependencies are in place before dependants are loaded.
*
* @param cartridges
* @returns {altair.Promise}
*/
addCartridges: function (cartridges) {
var deferred = new Deferred(),
deferred2 = new Deferred(),
started = [],
add = hitch(this, function () {
var cartridge = cartridges.shift();
if (cartridge) {
started.push(cartridge);
this.addCartridge(cartridge, false).then(add).otherwise(hitch(deferred, 'reject'));
} else {
deferred.resolve(this);
}
});
add();
//execute module
var execute = hitch(this, function () {
var cartridge = started.shift();
if (cartridge) {
when(cartridge.execute()).then(execute).otherwise(function (err) {
deferred2.reject(err);
});
} else {
deferred2.resolve(this);
}
});
return deferred.then(hitch(this, function () {
execute();
return deferred2;
}));
},
/**
* Teardown every cartridge.
*
* @returns {*}
*/
teardown: function () {
var l = _.map(this._cartridges, function (c) {
return c.teardown();
});
return all(l);
},
toString: function () {
return '[object Altair]';
}
});
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | 2 | /**
* What this class does differently than dojo/Deferred:
* * supports optional error logging suppression is reject()
* * step() alias to make for pretty codez
* * hasWaiting()
*/
define([
"dojo/has",
"dojo/_base/lang",
"dojo/errors/CancelError",
"dojo/promise/Promise",
'altair/plugins/node!debug',
"dojo/has!config-deferredInstrumentation?dojo/promise/instrumentation"
], function(has, lang, CancelError, Promise, debug, instrumentation){
debug.enable('altair:Deferred');
debug = debug('altair:Deferred');
// module:
// dojo/Deferred
var PROGRESS = 0,
RESOLVED = 1,
REJECTED = 2;
var FULFILLED_ERROR_MESSAGE = "This deferred has already been fulfilled.";
var freezeObject = Object.freeze || function(){};
var signalWaiting = function(waiting, type, result, rejection, deferred, ignoreErrors){
if(has("config-deferredInstrumentation")){
if(!ignoreErrors && type === REJECTED && Deferred.instrumentRejected && waiting.length === 0){
Deferred.instrumentRejected(result, false, rejection, deferred);
}
}
for(var i = 0; i < waiting.length; i++){
signalListener(waiting[i], type, result, rejection, ignoreErrors);
}
};
var signalListener = function(listener, type, result, rejection, ignoreErrors){
var func = listener[type];
var deferred = listener.deferred;
ignoreErrors = ignoreErrors || deferred.ignoreErrors;
if(func){
try{
//if this is a deferredsignaler, pass through error status
if(func.hasOwnProperty('__ignoreErrors')) {
func.__ignoreErrors = ignoreErrors;
}
var newResult = func(result);
if(type === PROGRESS){
if(typeof newResult !== "undefined"){
signalDeferred(deferred, type, newResult);
}
}else{
if(newResult && typeof newResult.then === "function"){
listener.cancel = newResult.cancel;
newResult.then(
// Only make resolvers if they're actually going to be used
makeDeferredSignaler(deferred, RESOLVED,ignoreErrors),
makeDeferredSignaler(deferred, REJECTED,ignoreErrors),
makeDeferredSignaler(deferred, PROGRESS,ignoreErrors));
return;
}
signalDeferred(deferred, RESOLVED, newResult, ignoreErrors);
}
}catch(error){
debug(error);
signalDeferred(deferred, REJECTED, error, ignoreErrors);
}
}else{
signalDeferred(deferred, type, result, ignoreErrors);
}
if(has("config-deferredInstrumentation")){
if(!ignoreErrors && type === REJECTED && Deferred.instrumentRejected){
Deferred.instrumentRejected(result, !!func, rejection, deferred.promise);
}
}
};
var makeDeferredSignaler = function(deferred, type, ignoreErrors){
var signaler = function(value){
signalDeferred(deferred, type, value, signaler.__ignoreErrors);
};
signaler.__ignoreErrors = ignoreErrors;
return signaler;
};
var signalDeferred = function(deferred, type, result, ignoreErrors){
ignoreErrors = ignoreErrors || deferred.ignoreErrors;
if(!deferred.isCanceled()){
switch(type){
case PROGRESS:
deferred.progress(result);
break;
case RESOLVED:
deferred.resolve(result);
break;
case REJECTED:
if(ignoreErrors) {
deferred.reject(result, false);
} else {
deferred.reject(result);
}
break;
}
}
};
var Deferred = function(canceler){
// summary:
// Creates a new deferred. This API is preferred over
// `dojo/_base/Deferred`.
// description:
// Creates a new deferred, as an abstraction over (primarily)
// asynchronous operations. The deferred is the private interface
// that should not be returned to calling code. That's what the
// `promise` is for. See `dojo/promise/Promise`.
// canceler: Function?
// Will be invoked if the deferred is canceled. The canceler
// receives the reason the deferred was canceled as its argument.
// The deferred is rejected with its return value, or a new
// `dojo/errors/CancelError` instance.
// promise: dojo/promise/Promise
// The public promise object that clients can add callbacks to.
var promise = this.promise = new Promise();
var deferred = this;
var fulfilled, result, rejection;
var canceled = false;
var waiting = [];
this.ignoreErrors = false;
if(!this.ignoreErrors && !has("config-deferredInstrumentation") && Error.captureStackTrace){
Error.captureStackTrace(deferred, Deferred);
Error.captureStackTrace(promise, Deferred);
}
this.isResolved = promise.isResolved = function(){
// summary:
// Checks whether the deferred has been resolved.
// returns: Boolean
return fulfilled === RESOLVED;
};
this.isRejected = promise.isRejected = function(){
// summary:
// Checks whether the deferred has been rejected.
// returns: Boolean
return fulfilled === REJECTED;
};
this.isFulfilled = promise.isFulfilled = function(){
// summary:
// Checks whether the deferred has been resolved or rejected.
// returns: Boolean
return !!fulfilled;
};
this.isCanceled = promise.isCanceled = function(){
// summary:
// Checks whether the deferred has been canceled.
// returns: Boolean
return canceled;
};
this.progress = function(update, strict){
// summary:
// Emit a progress update on the deferred.
// description:
// Emit a progress update on the deferred. Progress updates
// can be used to communicate updates about the asynchronous
// operation before it has finished.
// update: any
// The progress update. Passed to progbacks.
// strict: Boolean?
// If strict, will throw an error if the deferred has already
// been fulfilled and consequently no progress can be emitted.
// returns: dojo/promise/Promise
// Returns the original promise for the deferred.
if(!fulfilled){
signalWaiting(waiting, PROGRESS, update, null, deferred);
return promise;
}else if(strict === true){
throw new Error(FULFILLED_ERROR_MESSAGE);
}else{
return promise;
}
};
this.resolve = function(value, strict){
// summary:
// Resolve the deferred.
// description:
// Resolve the deferred, putting it in a success state.
// value: any
// The result of the deferred. Passed to callbacks.
// strict: Boolean?
// If strict, will throw an error if the deferred has already
// been fulfilled and consequently cannot be resolved.
// returns: dojo/promise/Promise
// Returns the original promise for the deferred.
if(!fulfilled){
// Set fulfilled, store value. After signaling waiting listeners unset
// waiting.
signalWaiting(waiting, fulfilled = RESOLVED, result = value, null, deferred);
waiting = null;
return promise;
}else if(strict === true){
throw new Error(FULFILLED_ERROR_MESSAGE);
}else{
return promise;
}
};
/**
* Now passing strict as false with disable error reporting
* @type {reject}
*/
var reject = this.reject = function(error, strict){
// summary:
// Reject the deferred.
// description:
// Reject the deferred, putting it in an error state.
// error: any
// The error result of the deferred. Passed to errbacks.
// strict: Boolean?
// If strict, will throw an error if the deferred has already
// been fulfilled and consequently cannot be rejected.
// returns: dojo/promise/Promise
// Returns the original promise for the deferred.
if(!fulfilled){
if(strict === false) {
this.ignoreErrors = true;
promise.ignoreErrors = true;
}
if(!this.ignoreErrors && has("config-deferredInstrumentation") && Error.captureStackTrace){
// error.__hasBeenLogged = true;
// console.error(error.stack || error);
Error.captureStackTrace(rejection = {}, reject);
}
signalWaiting(waiting, fulfilled = REJECTED, result = error, rejection, deferred, this.ignoreErrors);
waiting = null;
return promise;
}else if(strict === true){
throw new Error(FULFILLED_ERROR_MESSAGE);
}else{
return promise;
}
};
this.hasWaiting = function () {
return waiting.length > 0;
};
this.then = promise.then = function(callback, errback, progback){
// summary:
// Add new callbacks to the deferred.
// description:
// Add new callbacks to the deferred. Callbacks can be added
// before or after the deferred is fulfilled.
// callback: Function?
// Callback to be invoked when the promise is resolved.
// Receives the resolution value.
// errback: Function?
// Callback to be invoked when the promise is rejected.
// Receives the rejection error.
// progback: Function?
// Callback to be invoked when the promise emits a progress
// update. Receives the progress update.
// returns: dojo/promise/Promise
// Returns a new promise for the result of the callback(s).
// This can be used for chaining many asynchronous operations.
var listener = [progback, callback, errback];
// Ensure we cancel the promise we're waiting for, or if callback/errback
// have returned a promise, cancel that one.
listener.cancel = promise.cancel;
listener.deferred = new Deferred(function(reason){
// Check whether cancel is really available, returned promises are not
// required to expose `cancel`
return listener.cancel && listener.cancel(reason);
});
listener.deferred.ignoreErrors = this.ignoreErrors;
if(fulfilled && !waiting){
signalListener(listener, fulfilled, result, rejection);
}else{
waiting.push(listener);
}
return listener.deferred.promise;
};
this.otherwise = promise.step = function (errback, callback, progback) {
return this.then(callback, errback, progback);
};
this.step = promise.step = function (progback, callback ,errback) {
return this.then(callback, errback, progback);
};
this.always = function(callbackOrErrback){
// summary:
// Add a callback to be invoked when the promise is resolved
// or rejected.
// callbackOrErrback: Function?
// A function that is used both as a callback and errback.
// returns: dojo/promise/Promise
// Returns a new promise for the result of the callback/errback.
return this.then(callbackOrErrback, callbackOrErrback);
};
this.cancel = promise.cancel = function(reason, strict){
// summary:
// Inform the deferred it may cancel its asynchronous operation.
// description:
// Inform the deferred it may cancel its asynchronous operation.
// The deferred's (optional) canceler is invoked and the
// deferred will be left in a rejected state. Can affect other
// promises that originate with the same deferred.
// reason: any
// A message that may be sent to the deferred's canceler,
// explaining why it's being canceled.
// strict: Boolean?
// If strict, will throw an error if the deferred has already
// been fulfilled and consequently cannot be canceled.
// returns: any
// Returns the rejection reason if the deferred was canceled
// normally.
if(!fulfilled){
// Cancel can be called even after the deferred is fulfilled
if(canceler){
var returnedReason = canceler(reason);
reason = typeof returnedReason === "undefined" ? reason : returnedReason;
}
canceled = true;
if(!fulfilled){
// Allow canceler to provide its own reason, but fall back to a CancelError
if(typeof reason === "undefined"){
reason = new CancelError();
}
reject(reason);
return reason;
}else if(fulfilled === REJECTED && result === reason){
return reason;
}
}else if(strict === true){
throw new Error(FULFILLED_ERROR_MESSAGE);
}
};
freezeObject(promise);
};
Deferred.prototype.toString = function(){
// returns: String
// Returns `[object Deferred]`.
return "[object Deferred]";
};
if(instrumentation){
instrumentation(Deferred);
}
return Deferred;
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | 2 | /**
* Altair's base LifeCycle mixin. Anytime your object has a 3 part lifecycle (setup, execute, teardown)
* you should mixin this class. It returns dojo/Deferred's for each method for you, but chances are
* you'll need to override one of these methods. Each one, by default, will return an auto-resolved
* instance of a dojo/Deferred. That way usage is consistent, whether you override the method or not.
*
* Here is how I would implement my overridden startup()
*
* ...
*
* startup: function (options) {
*
* //options are optional, fallback to the ones already set
* options = options || this.options;
*
* //creating your own deferred will stop the auto-resolve behavior
* this.deferred = new Deferred();
*
* //run your complicated, time consuming setup
* fs.someLongComplicatedAsyncProcess(this.hitch(function (results) {
*
* //do something with your results
* this.results = results;
*
* //always resolve and pass yourself.
* this.deferred.resolve(this);
*
* }));
*
* return this.inherited(arguments);
*
* }
*
* ...
*
* REMEMBER: the purpose of startup() is to to ensure execute() has everything it needs to do its job. Don't abuse startup().
*
*/
define(['altair/facades/declare',
'./mixins/_DeferredMixin',
'lodash'],
function (declare,
_DeferredMixin) {
"use strict";
return declare([_DeferredMixin], {
deferred: null,
options: null,
//deferred tracking
_startupDeferred: null,
constructor: function (options) {
this.options = options;
},
/**
* Put anything that needs to be done (configuring, setup, etc.) before your lifecycle is executed.
*
* Startup always returns an altair/Deferred. If one does not exist (meaning you did not set this.deferred = new Deferred
* in your child class) then I will make one and resolve it immediately. This makes the operation synchronise,
* but will allow us to always use the startup().then(... syntax.
*
* @param options simply copied to local this.options
* @return {altair.Promise}
*/
startup: function (options) {
if(options) {
this.options = _.clone(options);
}
if(!this.deferred) {
this.deferred = new this.Deferred();
this.deferred.resolve(this);
}
//remove the deferred after it's been resolved
if(!this.deferred.always) {
this.deferred.reject(new Error('Invalid deferred type, use altair/Deferred.'));
} else {
this.deferred.always(this._deferredAutoRemover(this.deferred));
}
//tracking
this._startupDeferred = this.deferred;
return this.deferred.promise || this.deferred;
},
/**
* Mixin your async dependencies, for use during sartingy
*
* @param dependencies
* @returns {*}
*/
mixinDependencies: function (dependencies) {
this.deferred = this.all(dependencies).then(function (deps) {
declare.safeMixin(this, deps);
return this;
}.bind(this));
return this.deferred;
},
/**
* Do your work in here.
*
* @returns {altair.Promise}
*/
execute: function () {
//make sure startup deferred is still not active (can happen when someone goes life.startup().then(life.execute())
//and deferreds are auto-resolved (which makes the whole process sync)
if(this.deferred && this.deferred === this._startupDeferred) {
this.deferred = null;
}
if(!this.deferred) {
this.deferred = new this.Deferred();
this.deferred.resolve(this);
}
//do not need the startup deferred again
this._startupDeferred = null;
//remove the deferred after it's been resolved
this.deferred.always(this._deferredAutoRemover(this.deferred));
return this.deferred.promise || this.deferred;
},
/**
* Clean up so it's like you never existed.
*
* @returns {altair.Promise}
*/
teardown: function () {
if(!this.deferred) {
this.deferred = new this.Deferred();
this.deferred.resolve(this);
}
//remove the deferred after it's been resolved
this.deferred.always(this._deferredAutoRemover(this.deferred));
return this.deferred.promise || this.deferred;
},
_deferredAutoRemover: function(def) {
var scope = this;
return function () {
setTimeout(function () {
if(scope.deferred === def && scope.deferred.isResolved()) {
scope.deferred = null;
}
}, 0);
};
}
});
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | 2 | /**
* Simple State Machine.
*/
define(['altair/facades/declare',
'altair/facades/hitch',
'altair/events/Emitter',
'altair/events/Event',
'altair/Deferred',
'altair/facades/__',
'altair/facades/sprintf',
'lodash',
'altair/plugins/node!debug'
],
function (declare,
hitch,
Emitter,
Event,
Deferred,
__,
sprintf,
_,
debug) {
debug = debug('altair/StateMachine');
"use strict";
return declare([Emitter], {
state: null, //the my current state
states: null, //all our possible states
repeat: false, //run forever?
//statics
_listenerMap: {
'will-enter-state': 'onStateMachineWillEnter%s',
'did-enter-state': 'onStateMachineDidEnter%s',
'did-exit-state': 'onStateMachineDidExit%s'
},
constructor: function (options) {
this.state = options && _.has(options, 'state') ? options.state : this.state; //starting state is optional
this.states = options.states;
if(options && _.has(options, 'delegate')) {
this.attachListeners(options.delegate);
}
},
/**
* Attach all our possible listeners to delegate depending on whether or not they exist
*
* @param delegate
*/
attachListeners: function (delegate) {
var listeners = [];
//loop through each possible state
this.states.forEach(hitch(this, function (state) {
//then our listener map
Object.keys(this._listenerMap).forEach(hitch(this, function (eventName) {
//construct the callbacks name
var methodName = this._stateAndEventNameToCallbackName(state, eventName);
//does the delegate have the name? if so, lets add a listener for it
if(delegate[methodName] !== undefined) {
listeners.push(this.on(eventName, hitch(delegate, methodName), { state: state }));
}
}));
}));
return listeners;
},
/**
* Start me over!
*
* @returns {altair.events.Emitter}
*/
reset: function () {
this.state = this.states[0];
return this;
},
/**
* Utility to make it a tad easier to set listeners
*
* @param state
* @param eventName
* @returns {*}
* @private
*/
_stateAndEventNameToCallbackName: function (state, eventName) {
return sprintf(this._listenerMap[eventName], _.capitalize(state.replace(/-([a-z])/g, function (m, w) {
return w.toUpperCase();
})));
},
/**
* Loops through every state, emitting all events along the way. If an event listener returns a deferred, we
* wait until it is resolved before continuing. When we resolve our deferred, we will pass the return value
* of the last state we transitionedTo
*
* @param options - repeat: loop back on itself?
* @returns {altair.Deferred}
*/
execute: function (options) {
//lastResponse is is in form [state, data]... if no state, we're done
var d = new Deferred(),
state = options && _.has(options, 'state') ? options.state : this.states[0], //optionally override starting state
lastResponse = [state, {}],
fire = hitch(this, function () {
if(!lastResponse[0]) {
d.resolve(lastResponse[1]);
return;
}
this.transitionTo(lastResponse[0], lastResponse[1], options).then(function (response) {
lastResponse = response;
fire();
}).otherwise(hitch(this, function (err) {
debug('state machine error in state: ' + state);
debug(err);
d.reject({
error: err,
state: state
});
}));
});
//repeat option passthrough
if(_.has(options,'repeat')) {
this.repeat = options.repeat;
}
fire();
return d;
},
/**
* Pass me a state and I'll let you know which state is next... if you on the last state, i'll pass back the first
*
* @param state
* @param backToFirst
*
* @returns {*}
*/
nextState: function (state, backToFirst) {
var i = _.indexOf(this.states, state) + 1,
next = '';
if(i < this.states.length) {
next = this.states[i];
} else if(backToFirst === true){
next = this.states[0];
}
return next;
},
/**
* Same as above, but opposite order
*
* @param state
* @param backToLast
* @returns {string}
*/
previousState: function (state, backToLast) {
var i = _.indexOf(this.states, state) - 1,
next = '';
if(i >= 0) {
next = this.states[i];
} else if(backToLast === true){
next = this.states[this.states.length-1];
}
return next;
},
/**
* Transition to a particular state (which involves emitting all events that are the lifecycle of the state.
*
* @param state the state (must match something in this.states)
* @param data object passed through to event (usually whatever was returned from previous state)
*/
transitionTo: function (state, data, options) {
this.state = state;//probably should wait until didEnterState before setting this, yeah?
var dfd = new Deferred(),
eventData = data,
events = Object.keys(this._listenerMap),
lastResponse,
nextState = this.nextState(state, (options && _.has(options, 'repeat')) ? options.repeat : this.repeat),
fire = hitch(this, function (i) {
//are we on the last event?
if(i === events.length) {
dfd.resolve([nextState, lastResponse]);
return;
}
//make sure our event data is an object
if(!_.isObject(eventData)) {
eventData = {};
}
//make sure we at least have the state
eventData.state = state;
//emit this event
this.emit(events[i], eventData).then(hitch(this, function (e) {
//someone stopped the state machine
if(!e.active) {
this.repeat = false;
dfd.resolve([false, 'SIGABRT']);
return false;
}
var results = e.resultsRaw();
//get the results from the last event listener if any listeners are set
if(results.length > 0) {
lastResponse = results.pop();
//if lastResponse is in the form of [ nextState, { event: data } ]
//if so, halt the state machine and jump to that step immediately
if(_.isArray(lastResponse)) {
var _state = lastResponse[0];
if(_.indexOf(this.states, _state) === -1) {
dfd.reject(new Error(__('State "%s" does not exist on this state machine.', _state)));
}
nextState = _state;
eventData = lastResponse[1];
lastResponse = lastResponse[1];
dfd.resolve([nextState, lastResponse]);
return;
} else if(_.isObject(lastResponse)) {
eventData = lastResponse;
}
}
fire(++i);
})).otherwise(function (err) {
dfd.reject(err);
});
});
fire(0);
return dfd;
}
});
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 2 | /**
* Our simple test running. Works with doh/runner.
*/
define(['altair/facades/declare',
'altair/Lifecycle',
'altair/facades/hitch',
'altair/facades/all',
'altair/plugins/node!fs',
'altair/plugins/node!debug',
'altair/plugins/node!path',
'doh/runner',
'altair/facades/glob',
'require'],
function (declare,
Lifecycle,
hitch,
all,
fs,
debug,
path,
doh,
glob,
require) {
return declare('altair/TestRunner', [Lifecycle], {
/**
* @param options
* options.paths - an array of paths whose contents are assumed to be tests
*
* @returns {*}
*/
startup: function (options) {
var list = [],
_options = this.options || options;
// No tests to run throw error
if(!_options || !_options.glob) {
this.deferred.reject("You must pass glob option test runner to parse to look for tests.");
return;
}
doh.debug = console.log;
doh.error = function (err) {
if(err.stack) {
console.error(err.stack || err);
} else {
console.log(err);
}
};
doh._handleFailure = function (groupName, fixture, e) {
// this.debug("FAILED test:", fixture.name);
// mostly borrowed from JUM
this._groups[groupName].failures++;
if(e instanceof this._AssertFailure){
this._failureCount++;
this.error(e.toString());
}else{
this._errorCount++;
this.error(e); // printing Error on IE9 (and other browsers?) yields "[Object Error]"
}
if(fixture.runTest["toSource"]){
var ss = fixture.runTest.toSource();
this.debug("\tERROR IN:\n\t\t", ss);
}else{
this.debug("\tERROR IN:\n\t\t", fixture.runTest);
}
if(e.rhinoException){
e.rhinoException.printStackTrace();
}else if(e.javaException){
e.javaException.printStackTrace();
}
};
this.deferred = glob(_options.glob.map(function (p) {
if (p[0] === '.') {
return path.join(process.cwd(), p);
} else {
return require.toUrl(p);
}
}), _options.globOptions).then(hitch(this, function (files) {
list.concat(files.map(hitch(this, 'includeTest')));
return all(list);
})).then(hitch(this, function () {
return this;
}));
return this.inherited(arguments);
},
execute: function () {
this.deferred = new this.Deferred();
doh._onEnd = hitch(this, function () {
if(doh._errorCount > 0) {
this.deferred.reject(doh._errorCount + ' tests failed.');
} else {
this.deferred.resolve();
}
});
doh.run();
return this.inherited(arguments);
},
includeTest: function (path) {
var deferred = new this.Deferred();
require([path], function (t) {
if(!t) {
deferred.reject('including ' + path + ' failed');
} else {
deferred.resolve(t);
}
});
return deferred;
}
});
});
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| Apollo.js | 8.33% | (1 / 12) | 0% | (0 / 2) | 0% | (0 / 7) | 8.33% | (1 / 12) | |
| Collection.js | 50% | (1 / 2) | 100% | (0 / 0) | 0% | (0 / 1) | 50% | (1 / 2) | |
| Schema.js | 0.96% | (1 / 104) | 0% | (0 / 80) | 0% | (0 / 31) | 0.96% | (1 / 104) | |
| _HasSchemaMixin.js | 2.86% | (2 / 70) | 0% | (0 / 55) | 0% | (0 / 24) | 2.86% | (2 / 70) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 2 | /**
* Apollo is a unique ORM. It does not couple itself to any particular type of class, such as a data model or an entity.
* Schemas in Apollo are strict... very strict. This is to ensure that any schema has everything it needs to be rendered
* via a UI. Field options like "label" come in handy, even via cli and the "description"
* attribute is a great place for user friendly documentation (that engineers will appreciate too).
*/
define(['dojo/_base/declare',
'dojo/Deferred',
'dojo/_base/lang',
'./Schema'
], function (declare,
Deferred,
lang,
Schema) {
return declare('apollo/Apollo', null, {
propertyTypes: null,
constructor: function (propertyTypes) {
this.propertyTypes = {};
if(propertyTypes) {
this.addTypes(propertyTypes);
}
},
/**
* Add a new type of property
* @param fieldType {apollo.propertyTypes._Base}
* @returns {apollo.Apollo}
*/
addType: function (propertyType) {
this.propertyTypes[propertyType.key] = propertyType;
return this;
},
/**
* Add many property types at once.
*
* @param types
*/
addTypes: function (types) {
types.forEach(lang.hitch(this, function (type) {
this.addType(type);
}));
},
/**
* Get you a property type by key
*
* @param type
* @returns {*}
*/
type: function (key) {
return this.propertyTypes[key];
},
/**
* Creates a schema based on the data you provide, but uses the field types that have been added to Apollo
*
* @param data
* @returns {altair.io.core.lib.apollo.Schema}
*/
createSchema: function (data) {
var s = new Schema(data, this.propertyTypes);
return s;
}
});
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 2 | /**
* A collection is a wrapper for an array of _HasSchemaMixin objects to allow for chaining calls.
*
*
*
*/
define(['dojo/_base/declare',
'dojo/Deferred',
'dojo/_base/lang'
], function (declare, Deferred, lang) {
return declare(null, {
});
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | 2 | /**
* Apollo schemas are meant to be VERY lightweight.
*
* In altair
*
* var schema = this.nexus('cartridges/Apollo').createSchema({
* 'properties' => {
* 'fieldName' => {
* 'type' => 'string',
* 'options' => [
* 'label' => 'My cool field',
* 'description' => 'What the eff dude?',
* 'required' => true
* ]
* }
* });
*
*/
define(['dojo/_base/declare',
'dojo/_base/lang',
'dojo/Deferred',
'dojo/promise/all',
'lodash'
], function (declare, lang, Deferred, all, _) {
"use strict";
return declare(null, {
_data: null,
_propertyTypes: null,
_optionsByField: null,
_typeCache: null,
/**
* Pass through straight config... assume its setup how we like it.
*
* @param schema
*/
constructor: function (schema, propertyTypes) {
if (!schema || !propertyTypes) {
throw "You must pass a schema literal and an array of apollo/fieldtypes/_Base instances";
}
this._propertyTypes = {};
this._data = schema;
this._optionsByField = {};
this._typeCache = {};
if(!this._data.properties) {
this._data.properties = {};
}
//make sure each property has a name in it as well
_.each(this._data.properties, function (prop, name) {
prop.name = name;
});
if (propertyTypes instanceof Array) {
propertyTypes.forEach(lang.hitch(this, function (type) {
this._propertyTypes[type.key] = type;
}));
} else {
this._propertyTypes = propertyTypes;
}
},
/**
* Does a schema have a field by this name?
*
* @param propertyName
* @returns {boolean}
*/
has: function (propertyName) {
return (this._data.properties.hasOwnProperty(propertyName));
},
/**
* Tells you the type of a particular property (firstName would return string)
*
* @param propertyName
* @returns {string}
*/
typeFor: function (propertyName) {
return this._data.properties[propertyName].type;
},
/**
* Set a single option for a property
*
* @param propertyName
* @param optionName
* @param optionValue
* @returns {this}
*/
setOptionFor: function (propertyName, optionName, optionValue) {
if(!this.has(propertyName)) {
throw new Error('No property named ' + propertyName + ' exists on schema ');
}
this._data.properties[propertyName].options[optionName] = optionValue;
return this;
},
/**
* A property by a particular name
*
* @param named
* @returns {{}}
*/
property: function (named) {
return this._data.properties[named];
},
/**
* I single option for a property
*
* @param propertyName
* @param optionName
* @returns {*}
*/
optionFor: function (propertyName, optionName) {
if(!_.has(this._data.properties, propertyName)) {
throw new Error('no property called ' + propertyName + ' found on schema.');
}
return this._data.properties[propertyName].options[optionName];
},
/**
* Get you all the options for this field mixed in with all options for the field type.
*
* @param propertyName
* @param mixinAll optional
* @returns {string} all options for that field type
*/
optionsFor: function (propertyName, mixinAll) {
if (propertyName in this._data.properties) {
var property = this._data.properties[propertyName],
options = property.options;
if (!options) {
throw new Error(propertyName + " has no options. add it to your schema");
}
//if we are doing a simple (lightweight) get of options
if (mixinAll === false) {
return options;
}
if (!this._optionsByField[propertyName]) {
var type = this.propertyType(property.type);
this._optionsByField[propertyName] = type.normalizeOptions(options);
}
} else {
throw new Error(propertyName + ' does not exist on ' + this.declaredClass);
}
return this._optionsByField[propertyName];
},
option: function (named) {
return this._data[named];
},
get: function (named) {
return this._data[named];
},
data: function () {
return this._data;
},
primaryProperty: function () {
return _.where(this._data.properties, { type: 'primary' }).pop();
},
/**
* All the properties on this schema
*
* @returns {};
*/
properties: function () {
return this.option('properties');
},
/**
* Gets all properties in this schema, but returns an array
*
* @returns {Array}
*/
propertiesAsArray: function () {
var properties = [];
Object.keys(this._data.properties).forEach(lang.hitch(this, function (name) {
var property = lang.mixin({}, this._data.properties[name], {
name: name
});
properties.push(property);
}));
return properties;
},
/**
* Returns you a propertytype by a particular key, e.g. string, email, bool
*
* @param key
* @returns {*}
*/
propertyType: function (key) {
if (!(key in this._propertyTypes)) {
throw new Error('No property type of ' + key + ' found in schema.');
}
return this._propertyTypes[key];
},
/**
* All property types
*
* @returns {*}
*/
propertyTypes: function () {
return this._propertyTypes;
},
/**
* Apply a transformation strategy on many values at once (the keys of values must match a property in the schema)
*
* @param values
* @param optionsByProperty
* @param config
* @returns {*}
*/
applyOnValues: function (values, optionsByProperty, config) {
var _obp = optionsByProperty || {},
_config = config || {};
_.each(values, function (value, name) {
values[name] = this.applyOnProperty(_config.methods || ['toJsValue'], name, value, _obp[name], _config);
}, this);
return values;
},
/**
* Tries all the methods passed on the property propertyType, first one wins.
*
* Example:
*
* schema.applyOnProperty(['toSolrValue', 'toStringValue'], 'firstName', 'Taylo®™', { maxLength: 35 });
*
* @param named
* @returns {*}
*/
applyOnProperty: function (methodNames, propertyName, value, options, config) {
if (!this._data.properties[propertyName]) {
throw new Error('Property "' + propertyName + '" not found on ' + this + '.');
}
var property = this._data.properties[propertyName],
type = property.type,
_config = config || {},
propertyType = this.propertyType(type),
c,
methodName;
//by convention, these are null and will not be transformed
if ((value === null || value === undefined) && _config.ignoreNull !== false) {
return null;
}
//normalize options
options = lang.mixin({}, this.optionsFor(propertyName), options || {});
//normalize for many
value = propertyType.normalizeMany(value, options, config);
//invoke first method we find
for (c = 0; c < methodNames.length; c++) {
methodName = methodNames[c];
if (_.isFunction(propertyType[methodName])) {
//make sure it's not an array when {{methodName}} is called
if (propertyType.makeValuesSingular) {
var wasArray = false;
if (value instanceof Array) {
wasArray = true;
} else {
value = [value];
}
var finalValue = [];
value.forEach(function (_value) {
if ((_value === null || _value === undefined) && _config.ignoreNull !== false) {
//skip because null
} else {
finalValue.push(propertyType[methodName](_value, options, config));
}
});
return wasArray && finalValue.length > 0 ? finalValue : finalValue.length > 0 ? finalValue[0] : null;
}
//we want the raw value passed to method name
else {
return propertyType[methodName](value, options, config);
}
}
}
throw 'Could not find methods (' + methodNames.join(', ') + ') for property named "' + propertyName + '" of type "' + type + '".';
},
/**
* Add a new property to this schema.
*
* @param name
* @param type
* @param options
*/
append: function (name, type, options) {
this._data.properties[name] = {
type: type,
name: name,
options: options
};
return this;
},
/**
* Helpful printing
* @returns {string}
*/
toString: function () {
return '[object Schema]';
},
/**
* Validates all the values passed against our properties. Missing fields are also validaded with a value of null.
*
* schema().validate().then(... passs ...).otherwise(function (errs) {
* console.log('you have', errs.length, 'errors'); //outputs 'you have 6 errors'
* console.log('more details', errs.byProperty); //outputs `{ firstName: [...all errors...], email: [...all errors...] }
* });
*
* If no error occured, it will not be include in errs.byProperty.
*
* @param values the keys should match properties on this schema
* @param options { throwOnFirst: false, skipMissing: false } //should i return all errors, or just throw a new Error on first? - should i skip fields you did not pass?
* @returns {*|Promise}
*/
validate: function (values, options) {
var everything = {},
errors = [],
errorsByProp = {},
_options = options || {},
dfd = new Deferred(),
skipMissing = _.has(_options, 'skipMissing') ? _options.skipMissing : false,
_values = values || {};
//call validate on all properties
_.each(this.properties(), function (prop, key) {
//mantain keys for all validate() calls
if(!skipMissing || _.has(values, key)) {
everything[key] = this.applyOnProperty(['validate'], key, _values[key] || null, {}, { ignoreNull: false });
}
}, this);
//when all are done validating, check results for arrays (a result of true is pass, an array is fail)
all(everything).then(function (results) {
_.each(results, function (result, key) {
//anything with many: true will be
var many = this.optionFor(key, 'many'),
manyResults = many ? result : [result];
_.each(manyResults, function (result) {
//if the result of validate() is not true, it's an array of errors
if(result !== true) {
if(!_.isArray(result)) {
throw new Error('validate() for property type ' + this.typeFor(key) + ' must return `true` or an array.');
}
if(_options.throwOnFirst) {
throw new Error(result[0]);
}
errors = errors.concat(result);
if (!errorsByProp[key]) {
errorsByProp[key] = [];
}
errorsByProp[key] = errorsByProp[key].concat(result);
}
});
}, this);
if( errors.length > 0 ) {
errors.byProperty = errorsByProp;
dfd.reject(errors);
return;
}
dfd.resolve(true);
}.bind(this)).otherwise(function (err) {
dfd.reject(err);
});
return dfd;
}
});
});
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | 2 1 | /**
* Apollo _HasSchemaMixin -> give any object you want a schema, it's powerful and stuff =)
*/
define(['dojo/_base/declare',
'lodash',
'dojo/Deferred',
'dojo/promise/all',
'./Schema'
], function (declare,
_,
Deferred,
all,
Schema) {
function capitalise(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
var _HasSchemaMixin = declare(null, {
_schema: null,
values: null,
/**
* Pass a schema if ya'nt
*
* @param schema
*/
constructor: function (schema) {
if (schema && schema.isInstanceOf && schema.isInstanceOf(Schema)) {
this.setSchema(schema);
}
},
toGetter: function(named) {
return 'get' + capitalise(named);
},
toSetter: function(named) {
return 'set' + capitalise(named);
},
/**
* Will try and call an method to override this getter (getFieldName).
*
* @param name
* @param defaultValue
* @param options
* @param config
* @returns {*}
*/
get: function (name, defaultValue, options, config) {
var methodName = this.toGetter(name);
if (typeof this[methodName] === 'function') {
return this[methodName](defaultValue, options, config);
}
return this._get(name, defaultValue, options, config);
},
/**
* Will try and call an overridden setter first, then
*
* @param name
* @param value
* @returns {apollo|_HasSchemaMixin}
*/
set: function (name, value) {
var methodName = this.toSetter(name),
results;
if (typeof this[methodName] === 'function') {
results = this[methodName](value);
} else {
results = this._set(name, value);
}
return results;
},
/**
* Mixes in only matching values, leaving the rest
*
* @param values
* @returns {_HasSchemaMixin}
*/
mixin: function (values, optionsByField, config) {
var cleaned = {};
_.each(values, function (value, name) {
if (this.has(name)) {
if (config && config.methods) {
var options = optionsByField && optionsByField[name] || {};
//pass through old value
config.old = this.values[name];
//apply methods on value
cleaned[name] = this.schema().applyOnProperty(config.methods, name, value, options, config);
} else {
cleaned[name] = value;
}
}
}, this);
if (config && config.methods) {
return all(cleaned).then(function (values) {
_.each(values, function (value, name) {
this.set(name, value);
}, this);
return this;
}.bind(this));
} else {
_.each(cleaned, function (value, name) {
this.set(name, value);
}, this);
return this;
}
},
has: function (name) {
return this.schema().has(name);
},
/**
* Default setter, just sets values array
*
* @param name
* @param value
* @returns {apollo|_HasSchemaMixin}
* @private
*/
_set: function (name, value) {
if (this.schema().has(name)) {
this.values[name] = value;
} else {
throw new Error("No property called '" + name + "' exists on this " + this);
}
return this;
},
/**
* Last resort getter. If you override a getter and still want access to the original value in the schema
*
* @param name
* @param defaultValue
* @param options
* @param config
* @returns {*}
* @private
*/
_get: function (name, defaultValue, options, config) {
if (!_.has(this.values, name)) {
throw new Error('No property ' + name + ' found on ' + this);
}
var value = this.schema().applyOnProperty((config && config.methods) ? config.methods : ['toJsValue'], name, this.values[name], options, config);
if (value === null || value === undefined && !_.isUndefined(defaultValue)) {
value = defaultValue;
}
return value;
},
/**
* Set the schema to this object, then set default values to ourselves.
*
* @param schema
* @returns {apollo|_HasSchemaMixin}
*/
setSchema: function (schema) {
this._schema = schema;
if (!this.values) {
this.values = {};
}
var properties = schema.properties();
_.each(properties, function (value, name) {
//only set values on ourselves that do not already exist
//this is to ensure that values has a key for every property in the schema
if (!( this.values.hasOwnProperty(name) )) {
var defaultValue = schema.optionsFor(name, false).default;
this.set(name, defaultValue);
}
}, this);
return this;
},
/**
* Returns you the schema attached to this object
*
* @returns {apollo.Schema}
*/
schema: function () {
return this._schema;
},
/**
*
* @returns {*}
*/
getValues: function (optionsByProperty, config) {
var values = {},
_obp = optionsByProperty || {},
_all = _obp['*'] || {},
_config = config || {};
_.each(this.schema().properties(), function (propConfig, name) {
if (_obp) {
_obp[name] = _.defaults(_obp[name] || {}, _all);
}
var options = _.defaults(_obp[name] || {}, propConfig.options);
values[name] = this.get(name, null, options, _config);
}, this);
return values;
},
primaryProperty: function () {
return this.schema().primaryProperty();
},
primaryValue: function () {
var prop = this.primaryProperty();
return prop ? this.get(prop.name) : undefined;
},
/**
* @TODO finish
*
* @returns {dojo.Deferred}
*/
validate: function (options) {
var schema = this.schema();
if (!schema) {
throw new Error('You can only validate objects that have a schema.');
}
return schema.validate(this.values, options).then(function () {
return this;
}.bind(this));
}
});
return _HasSchemaMixin;
});
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| _browserRunner.js | 0.18% | (1 / 546) | 0% | (0 / 211) | 0% | (0 / 65) | 0.19% | (1 / 539) | |
| _nodeRunner.js | 4.76% | (1 / 21) | 0% | (0 / 8) | 0% | (0 / 3) | 4.76% | (1 / 21) | |
| _parseURLargs.js | 5.08% | (3 / 59) | 0% | (0 / 27) | 11.11% | (1 / 9) | 5.08% | (3 / 59) | |
| _rhinoRunner.js | 5% | (1 / 20) | 0% | (0 / 8) | 0% | (0 / 3) | 5% | (1 / 20) | |
| doh.profile.js | 33.33% | (2 / 6) | 0% | (0 / 3) | 0% | (0 / 4) | 33.33% | (2 / 6) | |
| main.js | 50% | (1 / 2) | 100% | (0 / 0) | 0% | (0 / 1) | 50% | (1 / 2) | |
| robot.js | 2.23% | (4 / 179) | 0% | (0 / 116) | 0% | (0 / 49) | 2.37% | (4 / 169) | |
| runner.js | 0.41% | (3 / 729) | 0% | (0 / 411) | 0% | (0 / 133) | 0.42% | (3 / 715) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 | 2 | define([ "dojo/dom", "dojo/dom-geometry", "dojo/dom-style", "dojo/_base/fx", "dojo/_base/lang", "dojo/query", "dojo/domReady", "dojo/sniff", "dojo/window", "doh/runner" ], function(dom, domGeom, domStyle, baseFx, lang, query, domReady, has, win, doh){ doh.isBrowser= true; var topdog; try{ topdog = (window.parent == window) || !Boolean(window.parent.doh); }catch(e){ //can't access window.parent.doh, then consider ourselves as topdog topdog=true; } if(topdog){ // we're the top-dog window. // borrowed from Dojo, etc. var byId = function(id){ return document.getElementById(id); }; var _addOnEvt = function( type, // string refOrName, // function or string scope){ // object, defaults is window if(!scope){ scope = window; } var funcRef = refOrName; if(typeof refOrName == "string"){ funcRef = scope[refOrName]; } var enclosedFunc = function(){ return funcRef.apply(scope, arguments); }; if(domReady && type == "load"){ domReady(enclosedFunc); }else{ if(window["attachEvent"]){ window.attachEvent("on"+type, enclosedFunc); }else if(window["addEventListener"]){ window.addEventListener(type, enclosedFunc, false); }else if(document["addEventListener"]){ document.addEventListener(type, enclosedFunc, false); } } }; // // Over-ride or implement base runner.js-provided methods // var escapeXml = function(str){ // summary: // Adds escape sequences for special characters in XML: &<>"' // Optionally skips escapes for single quotes return str.replace(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, """); // string }; var formatTime = function(n){ switch(true){ case n<1000: //<1s return n+"ms"; case n<60000: //<1m return Math.round(n/100)/10+"s"; case n<3600000: //<1h return Math.round(n/6000)/10+"m"; default: //>1h return Math.round(n/360000)/10+"h"; } }; var _logBacklog = [], _loggedMsgLen = 0; var sendToLogPane = function(args, skip){ var msg = ""; for(var x = 0; x < args.length; x++){ msg += " " + args[x]; } msg = escapeXml(msg); // workarounds for IE. Wheeee!!! msg = msg.replace("\t", " ") .replace(" ", " ") .replace("\n", "<br> "); if(!byId("logBody")){ _logBacklog.push(msg); return; }else if(_logBacklog.length && !skip){ var tm; while((tm = _logBacklog.shift())){ sendToLogPane(tm, true); } } var logBody = byId("logBody"); var tn = document.createElement("div"); tn.innerHTML = msg; //tn.id="logmsg_"+logBody.childNodes.length; logBody.appendChild(tn); _loggedMsgLen++; }; var findTarget = function(n){ while(n && !n.getAttribute('_target')){ n = n.parentNode; if(!n.getAttribute){ n = null; } } return n; }; doh._jumpToLog = function(e){ //console.log(e); var node = findTarget(e?e.target:window.event.srcElement); if(!node){ return; } var _t = Number(node.getAttribute('_target')); var lb = byId("logBody"); if(_t>=lb.childNodes.length){ return; } var t = lb.childNodes[_t]; t.scrollIntoView(); if(domStyle && baseFx){ //t.parentNode.parentNode is <div class="tabBody">, only it has a explicitly set background-color, //all children of it are transparent var bgColor = domStyle.get(t.parentNode.parentNode,'backgroundColor'); //node.parentNode is the tr which has background-color set explicitly var hicolor = domStyle.get(node.parentNode,'backgroundColor'); var unhilight = baseFx.animateProperty({ node: t, duration: 500, properties: { backgroundColor: { start: hicolor, end: bgColor } }, onEnd: function(){ t.style.backgroundColor=""; } }); var hilight = baseFx.animateProperty({ node: t, duration: 500, properties: { backgroundColor: { start: bgColor, end: hicolor } }, onEnd: function(){ unhilight.play(); } }); hilight.play(); } }; doh._jumpToSuite = function(e){ var node = findTarget(e ? e.target : window.event.srcElement); if(!node){ return; } var _g = node.getAttribute('_target'); var gn = getGroupNode(_g); if(!gn){ return; } gn.scrollIntoView(); }; doh._init = (function(oi){ return function(){ var lb = byId("logBody"); if(lb){ // clear the console before each run while(lb.firstChild){ lb.removeChild(lb.firstChild); } _loggedMsgLen = 0; } this._totalTime = 0; this._suiteCount = 0; oi.apply(doh, arguments); } })(doh._init); doh._setupGroupForRun = (function(os){ //overload _setupGroupForRun to record which log line to jump to when a suite is clicked return function(groupName){ var tg = doh._groups[groupName]; doh._curTestCount = tg.length; doh._curGroupCount = 1; var gn = getGroupNode(groupName); if(gn){ //two lines will be added, scroll the second line into view gn.getElementsByTagName("td")[2].setAttribute('_target',_loggedMsgLen+1); } os.apply(doh,arguments); } })(doh._setupGroupForRun); var originalDohReport= doh._report; doh._report = function(){ var tb = byId("testList"); if(tb){ var tfoots=tb.getElementsByTagName('tfoot'); if(tfoots.length){ tb.removeChild(tfoots[0]); } var foot = tb.createTFoot(); var row = foot.insertRow(-1); row.className = 'inProgress'; var cell=row.insertCell(-1); cell.colSpan=2; cell.innerHTML="Result"; cell = row.insertCell(-1); cell.innerHTML=this._testCount+" tests in "+this._groupCount+" groups /<span class='failure'>"+this._errorCount+"</span> errors, <span class='failure'>"+this._failureCount+"</span> failures"; cell.setAttribute('_target',_loggedMsgLen+1); row.insertCell(-1).innerHTML=formatTime(doh._totalTime); } //This location can do the final performance rendering for the results //of any performance tests. if(doh.perfTestResults){ require(["dojox/math/stats", "dojox/charting/DataChart", "dojox/charting/plot2d/Scatter", "dojox/charting/plot2d/Lines", "dojo/data/ItemFileReadStore"], function(stats, DataChart, Scatter, Lines, ItemFileReadStore){ lang.mixin(doh, stats); var plotResults = function(div, name, dataArray) { // Performance report generating functions! var median = doh.median(dataArray); var medarray = []; var i; for(i = 0; i < dataArray.length; i++){ medarray.push(median); } var data = { label: "name", items: [ {name: name, trials: dataArray}, {name: "Median", trials: medarray} ] }; var ifs = new ItemFileReadStore({data: data}); var min = Math.floor(doh.min(dataArray)); var max = Math.ceil(doh.max(dataArray)); var step = (max - min)/10; //Lets try to pad out the bottom and top a bit //Then recalc the step. if(min > 0){ min = min - step; if(min < 0){ min = 0; } min = Math.floor(min); } if(max > 0){ max = max + step; max = Math.ceil(max); } step = (max - min)/10; var chart = new DataChart(div, { type: Lines, displayRange: dataArray.length, xaxis: {min: 1, max: dataArray.length, majorTickStep: Math.ceil((dataArray.length - 1)/10), htmlLabels: false}, yaxis: {min: min, max: max, majorTickStep: step, vertical: true, htmlLabels: false} }); chart.setStore(ifs, {name:"*"}, "trials"); }; try{ var g; var pBody = byId("perfTestsBody"); var chartsToRender = []; // store analytics for reading later // keyed on test group name, each value is in turn an object keyed on test name doh.perfTestAnalytics={}; doh.showPerfTestsPage(); for(g in doh.perfTestResults){ doh.perfTestAnalytics[g] = {}; var grp = doh.perfTestResults[g]; var hdr = document.createElement("h1"); hdr.appendChild(document.createTextNode("Group: " + g)); pBody.appendChild(hdr); var ind = document.createElement("blockquote"); pBody.appendChild(ind); var f; for(f in grp){ var fResults = grp[f]; if(!fResults){ continue; } var fhdr = document.createElement("h3"); fhdr.appendChild(document.createTextNode("TEST: " + f)); fhdr.style.textDecoration = "underline"; ind.appendChild(fhdr); var div = document.createElement("div"); ind.appendChild(div); //Figure out the basic info var results = "<b>TRIAL SIZE: </b>" + fResults.trials[0].testIterations + " iterations<br>" + "<b>NUMBER OF TRIALS: </b>" + fResults.trials.length + "<br>"; //Figure out the average test pass cost. var i; var iAvgArray = []; var tAvgArray = []; for(i = 0; i < fResults.trials.length; i++){ iAvgArray.push(fResults.trials[i].average); tAvgArray.push(fResults.trials[i].executionTime); } var analytics = doh.perfTestAnalytics[g][f] = { averageTrialExecutionTime: doh.mean(tAvgArray), maxTestIterationTime: doh.max(iAvgArray), minTestIterationTime: doh.min(iAvgArray), averageTestIterationTime: doh.mean(iAvgArray), medianTestIterationTime: doh.median(iAvgArray), varianceTestIterationTime: doh.variance(iAvgArray), standardDeviationTestIterationTime: doh.sd(iAvgArray) }; results += "<b>AVERAGE TRIAL EXECUTION TIME: </b>" + analytics.averageTrialExecutionTime.toFixed(10) + "ms.<br>"; results += "<b>MAXIMUM TEST ITERATION TIME: </b>" + analytics.maxTestIterationTime.toFixed(10) + "ms.<br>"; results += "<b>MINIMUM TEST ITERATION TIME: </b>" + analytics.minTestIterationTime.toFixed(10) + "ms.<br>"; results += "<b>AVERAGE TEST ITERATION TIME: </b>" + analytics.averageTestIterationTime.toFixed(10) + "ms.<br>"; results += "<b>MEDIAN TEST ITERATION TIME: </b>" + analytics.medianTestIterationTime.toFixed(10) + "ms.<br>"; results += "<b>VARIANCE TEST ITERATION TIME: </b>" + analytics.varianceTestIterationTime.toFixed(10) + "ms.<br>"; results += "<b>STANDARD DEVIATION ON TEST ITERATION TIME: </b>" +analytics.standardDeviationTestIterationTime.toFixed(10) + "ms.<br>"; //Okay, attach it all in. div.innerHTML = results; div = document.createElement("div"); div.innerHTML = "<h3>Average Test Execution Time (in milliseconds, with median line)</h3>"; ind.appendChild(div); div = document.createElement("div"); domStyle.set(div, "width", "600px"); domStyle.set(div, "height", "250px"); ind.appendChild(div); chartsToRender.push({ div: div, title: "Average Test Execution Time", data: iAvgArray }); div = document.createElement("div"); div.innerHTML = "<h3>Average Trial Execution Time (in milliseconds, with median line)</h3>"; ind.appendChild(div); div = document.createElement("div"); domStyle.set(div, "width", "600px"); domStyle.set(div, "height", "250px"); ind.appendChild(div); chartsToRender.push({ div: div, title: "Average Trial Execution Time", data: tAvgArray }); } } //Lazy-render these to give the browser time and not appear locked. var delayedRenders = function() { if(chartsToRender.length){ var chartData = chartsToRender.shift(); plotResults(chartData.div, chartData.title, chartData.data); } setTimeout(delayedRenders, 50); }; setTimeout(delayedRenders, 150); }catch(e){ doh.debug(e); } }); } originalDohReport.apply(doh,arguments); }; doh.error = undefined; if(this["opera"] && opera.postError){ doh.debug = function(){ var msg = ""; for(var x=0; x<arguments.length; x++){ msg += " "+arguments[x]; } sendToLogPane([msg]); opera.postError("DEBUG:"+msg); } }else if(window["console"]){ if(console.error){ doh.error = function(){ sendToLogPane.call(window, arguments); console.error(Array.prototype.join.call(arguments, " ")) }; } if(console.debug){ doh.debug = function(){ sendToLogPane.call(window, arguments); console.debug(Array.prototype.join.call(arguments, " ")) }; }else if(console.info){ doh.debug = function(){ sendToLogPane.call(window, arguments); console.info(Array.prototype.join.call(arguments, " ")) }; }else{ doh.debug = function(){ sendToLogPane.call(window, arguments); console.log("DEBUG:"+ Array.prototype.join.call(arguments, " ")); }; } }else{ doh.debug = function(){ sendToLogPane.call(window, arguments); } } doh.error = doh.error || doh.debug; var loaded = false; var groupTemplate = null; var testTemplate = null; var groupNodes = {}; var _groupTogglers = {}; var _getGroupToggler = function(group, toggle){ if(_groupTogglers[group]){ return _groupTogglers[group]; } var rolledUp = true; return (_groupTogglers[group] = function(evt, forceOpen){ var nodes = groupNodes[group].__items; var x; if(rolledUp||forceOpen){ rolledUp = false; for(x=0; x<nodes.length; x++){ nodes[x].style.display = ""; } toggle.innerHTML = "▼"; }else{ rolledUp = true; for(x=0; x<nodes.length; x++){ nodes[x].style.display = "none"; } toggle.innerHTML = "►"; } }); }; var addGroupToList = function(group){ if(!byId("testList")){ return; } var tb = byId("testList").tBodies[0]; var tg = groupTemplate.cloneNode(true); var tds = tg.getElementsByTagName("td"); var toggle = tds[0]; toggle.onclick = _getGroupToggler(group, toggle); var cb = tds[1].getElementsByTagName("input")[0]; cb.group = group; cb.onclick = function(){ doh._groups[group].skip = (!this.checked); }; tds[2].innerHTML = "<div class='testGroupName'>" + group + "</div><div style='width:0;'> </div>"; tds[3].innerHTML = ""; tb.appendChild(tg); return tg; }; var addFixtureToList = function(group, fixture){ if(!testTemplate){ return; } var cgn = groupNodes[group]; if(!cgn["__items"]){ cgn.__items = []; } var tn = testTemplate.cloneNode(true); var tds = tn.getElementsByTagName("td"); tds[2].innerHTML = fixture.name; tds[3].innerHTML = ""; var nn = (cgn.__lastFixture || cgn.__groupNode).nextSibling; if(nn){ nn.parentNode.insertBefore(tn, nn); }else{ cgn.__groupNode.parentNode.appendChild(tn); } // FIXME: need to make group display toggleable!! tn.style.display = "none"; cgn.__items.push(tn); return (cgn.__lastFixture = tn); }; var getFixtureNode = function(group, fixture){ if(groupNodes[group]){ return groupNodes[group][fixture.name]; } return null; }; var getGroupNode = function(group){ if(groupNodes[group]){ return groupNodes[group].__groupNode; } return null; }; var updateBacklog = []; doh._updateTestList = function(group, fixture, unwindingBacklog){ if(!loaded){ if(group && fixture){ updateBacklog.push([group, fixture]); } return; }else if(updateBacklog.length && !unwindingBacklog){ var tr; while((tr = updateBacklog.shift())){ doh._updateTestList(tr[0], tr[1], true); } } if(group && fixture){ if(!groupNodes[group]){ groupNodes[group] = { "__groupNode": addGroupToList(group) }; } if(!groupNodes[group][fixture.name]){ groupNodes[group][fixture.name] = addFixtureToList(group, fixture) } } }; doh._testRegistered = doh._updateTestList; doh._groupStarted = function(group){ if(this._suiteCount == 0){ this._runedSuite = 0; this._currentGlobalProgressBarWidth = 0; this._suiteCount = this._testCount; } // console.debug("_groupStarted", group); if(doh._inGroup != group){ doh._groupTotalTime = 0; doh._runed = 0; doh._inGroup = group; this._runedSuite++; } var gn = getGroupNode(group); if(gn){ gn.className = "inProgress"; } }; doh._groupFinished = function(group, success){ // console.debug("_groupFinished", group); var gn = getGroupNode(group); if(gn && doh._inGroup == group){ doh._totalTime += doh._groupTotalTime; gn.getElementsByTagName("td")[3].innerHTML = formatTime(doh._groupTotalTime); gn.getElementsByTagName("td")[2].lastChild.className = ""; doh._inGroup = null; //doh._runedSuite++; var failure = doh._updateGlobalProgressBar(this._runedSuite / this._groupCount, success, group); gn.className = failure ? "failure" : "success"; //doh._runedSuite--; doh._currentGlobalProgressBarWidth = parseInt(this._runedSuite / this._groupCount * 10000) / 100; //byId("progressOuter").style.width = parseInt(this._runedSuite/this._suiteCount*100)+"%"; } if(doh._inGroup == group){ this.debug("Total time for GROUP \"", group, "\" is ", formatTime(doh._groupTotalTime)); } }; doh._testStarted = function(group, fixture){ // console.debug("_testStarted", group, fixture.name); var fn = getFixtureNode(group, fixture); if(fn){ fn.className = "inProgress"; } }; var _nameTimes = {}; var _playSound = function(name){ if(byId("hiddenAudio") && byId("audio") && byId("audio").checked){ // console.debug("playing:", name); var nt = _nameTimes[name]; // only play sounds once every second or so if((!nt) || (((new Date) - nt) > 700)){ _nameTimes[name] = new Date(); var tc = document.createElement("span"); byId("hiddenAudio").appendChild(tc); tc.innerHTML = '<embed src="_sounds/' + name + '.wav" autostart="true" loop="false" hidden="true" width="1" height="1"></embed>'; } } }; doh._updateGlobalProgressBar = function(p, success, group){ var outerContainer = byId("progressOuter"); var gdiv = outerContainer.childNodes[doh._runedSuite - 1]; if(!gdiv){ gdiv = document.createElement('div'); outerContainer.appendChild(gdiv); gdiv.className = 'success'; gdiv.setAttribute('_target', group); } if(!success && !gdiv._failure){ gdiv._failure = true; gdiv.className = 'failure'; if(group){ gdiv.setAttribute('title', 'failed group ' + group); } } var tp = parseInt(p * 10000) / 100; gdiv.style.width = (tp - doh._currentGlobalProgressBarWidth) + "%"; return gdiv._failure; }; doh._testFinished = function(group, fixture, success){ var fn = getFixtureNode(group, fixture); var elapsed = fixture.endTime-fixture.startTime; var gn; if(fn){ fn.getElementsByTagName("td")[3].innerHTML = formatTime(elapsed); fn.className = (success) ? "success" : "failure"; fn.getElementsByTagName("td")[2].setAttribute('_target', _loggedMsgLen); if(!success){ _playSound("doh"); gn = getGroupNode(group); if(gn){ gn.className = "failure"; _getGroupToggler(group)(null, true); } } } if(doh._inGroup == group){ gn = getGroupNode(group); doh._runed++; if(gn && doh._curTestCount){ var p = doh._runed/doh._curTestCount; var groupfail = this._updateGlobalProgressBar((doh._runedSuite+p-1)/doh._groupCount,success,group); var pbar = gn.getElementsByTagName("td")[2].lastChild; pbar.className = groupfail?"failure":"success"; pbar.style.width = parseInt(p*100)+"%"; gn.getElementsByTagName("td")[3].innerHTML = parseInt(p*10000)/100+"%"; } } this._groupTotalTime += elapsed; this.debug((success ? "PASSED" : "FAILED"), "test:", fixture.name, elapsed, 'ms'); }; doh._registerUrl = function(group, url, timeout, type, dohArgs){ group= group || url; this._registerTest(group, { name: url, setUp: function(){ doh.currentGroupName = group; doh.currentGroup = this; doh.currentUrl = url; doh.dohArgs = dohArgs; this.d = new doh.Deferred(); doh.currentTestDeferred = this.d; doh.showTestPage(); byId("testBody").src = url; }, timeout: timeout||10000, // 10s // timeout: timeout||1000, // 10s runTest: function(){ // FIXME: implement calling into the url's groups here!! return this.d; }, tearDown: function(){ doh.currentGroupName = null; doh.currentGroup = null; doh.currentTestDeferred = null; doh.currentUrl = null; // this.d.errback(false); // byId("testBody").src = "about:blank"; doh.showLogPage(); } }, type); }; // // Utility code for runner.html // // var isSafari = navigator.appVersion.indexOf("Safari") >= 0; var tabzidx = 1; var _showTab = function(toShow, toHide){ // FIXME: I don't like hiding things this way. var i; for(i = 0; i < toHide.length; i++){ var node = byId(toHide[i]); if(node){ node.style.display = "none"; } } toShow = byId(toShow); if(toShow){ toShow.style.display = ""; toShow.style.zIndex = ++tabzidx; } }; doh.showTestPage = function(){ _showTab("testBody", ["logBody", "perfTestsBody"]); }; doh.showLogPage = function(){ _showTab("logBody", ["testBody", "perfTestsBody"]); }; doh.showPerfTestsPage = function(){ _showTab("perfTestsBody", ["testBody", "logBody"]); }; var runAll = true; doh.toggleRunAll = function(){ // would be easier w/ query...sigh runAll = !runAll; if(!byId("testList")){ return; } var tb = byId("testList").tBodies[0]; var inputs = tb.getElementsByTagName("input"); var x = 0; var tn; while((tn = inputs[x++])){ tn.checked = runAll; doh._groups[tn.group].skip = (!runAll); } }; var listHeightTimer = null; var setListHeight = function(){ if(listHeightTimer){ clearTimeout(listHeightTimer); } var tl = byId("testList"); if(!tl){ return; } listHeightTimer = setTimeout(function(){ tl.style.display = "none"; tl.style.display = ""; }, 10); }; _addOnEvt("resize", setListHeight); _addOnEvt("load", setListHeight); _addOnEvt("load", function(){ if(loaded){ return; } loaded = true; groupTemplate = byId("groupTemplate"); if(!groupTemplate){ // make sure we've got an amenable DOM structure return; } groupTemplate.parentNode.removeChild(groupTemplate); groupTemplate.style.display = ""; testTemplate = byId("testTemplate"); testTemplate.parentNode.removeChild(testTemplate); testTemplate.style.display = ""; doh._updateTestList(); }); _addOnEvt("load", function(){ // let robot code run if it gets to this first var __onEnd = doh._onEnd; doh._onEnd = function(){ __onEnd.apply(doh, arguments); if(doh._failureCount == 0){ doh.debug("WOOHOO!!"); _playSound("woohoo"); }else{ console.debug("doh._failureCount:", doh._failureCount); } if(byId("play")){ toggleRunning(); } }; if(!byId("play")){ // make sure we've got an amenable DOM structure return; } var isRunning = false; var toggleRunning = function(){ // ugg, this would be so much better w/ dojo.query() if(isRunning){ byId("play").style.display = byId("pausedMsg").style.display = ""; byId("playingMsg").style.display = byId("pause").style.display = "none"; isRunning = false; }else{ byId("play").style.display = byId("pausedMsg").style.display = "none"; byId("playingMsg").style.display = byId("pause").style.display = ""; isRunning = true; } }; doh.run = (function(oldRun){ return function(){ if(!doh._currentGroup){ toggleRunning(); } return oldRun.apply(doh, arguments); } })(doh.run); var btns = byId("toggleButtons").getElementsByTagName("span"); var node; var idx=0; while((node=btns[idx++])){ node.onclick = toggleRunning; } } ); }else{ // we're in an iframe environment. Time to mix it up a bit. var _doh = window.parent.doh; var _thisGroup = _doh.currentGroupName; var _thisUrl = _doh.currentUrl; if(_thisGroup){ doh._onEnd = function(){ _doh._errorCount += doh._errorCount; _doh._failureCount += doh._failureCount; _doh._testCount += doh._testCount; // should we be really adding raw group counts? //_doh._groupCount += doh._groupCount; _doh.currentTestDeferred.callback(true); }; doh._testRegistered = function(group, fixture){ fixture.name = _thisUrl+"::"+arguments[0]+"::"+fixture.name; _doh._updateTestList(_thisGroup, fixture); }; doh.debug = lang.hitch(_doh, "debug"); doh.error = lang.hitch(_doh, "error"); doh.registerUrl = lang.hitch(_doh, "registerUrl"); doh._testStarted = function(group, fixture){ _doh._testStarted(_thisGroup, fixture); }; doh._testFinished = function(g, f, s){ _doh._testFinished(_thisGroup, f, s); //Okay, there may be performance info we need to filter back //to the parent, so do that here. if(doh.perfTestResults){ try{ var gName = g.toString(); var localFName = f.name; while(localFName.indexOf("::") >= 0){ localFName = localFName.substring(localFName.indexOf("::") + 2, localFName.length); } if(!_doh.perfTestResults){ _doh.perfTestResults = {}; } if(!_doh.perfTestResults[gName]){ _doh.perfTestResults[gName] = {}; } _doh.perfTestResults[gName][f.name] = doh.perfTestResults[gName][localFName]; }catch (e){ doh.debug(e); } } }; doh._groupStarted = function(){ if(!this._setParent){ _doh._curTestCount = this._testCount; _doh._curGroupCount = this._groupCount; this._setParent = true; } }; doh._report = function(){ }; } } var fixHeight = doh._fixHeight = function(){ // IE9 doesn't give test iframe height because no nodes have an explicit pixel height! // Give outer table a pixel height. if(has("ie")){ var headerHeight = 0; var rows = query('#testLayout > tbody > tr'); for(var i = 0; i < rows.length-1; i++){ headerHeight += domGeom.position(rows[i]).h; } try{ // we subtract the headerHeight from the window height because the table row containing the tests is height:100% so they will stretch the table to the intended height. dom.byId('testLayout').style.height = (win.getBox().h - headerHeight)+"px"; }catch(e){ // An obscure race condition when you load the runner in IE from the command line causes the window reported height to be 0. // Try to recover after the window finishes rendering. setTimeout(function(){ fixHeight(); },0); } } }; return doh; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 2 | define(["doh/runner", "require", "dojo/_base/config"], function(doh, require, config){ /*===== return { // summary: // Module for running DOH tests in node (as opposed to a browser). // Augments return value from doh/runner. }; =====*/ doh.debug= console.log; doh.error= console.log; // Override the doh._report method to make it quit with an // appropriate exit code in case of test failures. var oldReport = doh._report; doh._report = function(){ oldReport.apply(doh, arguments); if(this._failureCount > 0 || this._errorCount > 0){ process.exit(1); } }; console.log("\n"+doh._line); console.log("The Dojo Unit Test Harness, $Rev: e6490e5 $"); console.log("Copyright (c) 2011, The Dojo Foundation, All Rights Reserved"); console.log("Running with node.js"); for (var tests= [], args= config["commandLineArgs"], i= 0, arg; i<args.length; i++) { arg= args[i]; if (arg.length==2 && arg[0]=="test") { var test= arg[1]; console.log("loading test " + test); tests.push(test); } } console.log(doh._line, "\n"); require(tests, function() { doh.run(); }); }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | 2 2 2 | (function(){
var
boot =
// zero to many scripts to load a configuration and/or loader.
// i.e. path-to-util/doh/runner.html?boots=path-to/config.js,path-to/require.js
["../../dojo/dojo.js"],
standardDojoBoot = boot,
test =
// zero to many AMD modules and/or URLs to load; provided by csv URL query parameter="test"
// For example, the URL...
//
// path-to-util/doh/runner.html?test=doh/selfTest,my/path/test.js
//
// ...will load...
//
// * the AMD module doh/selfTest
// * the plain old Javascript resource my/path/test.js
//
["dojo/tests/module"],
paths =
// zero to many path items to pass to the AMD loader; provided by semicolon separated values
// for URL query parameter="paths"; each path item has the form <from-path>,<to-path>
// i.e. path-to-util/doh/runner.html?paths=my/from/path,my/to/path;my/from/path2,my/to/path2
{},
dohPlugins =
// Semicolon separated list of files to load before the tests.
// Idea is to override aspects of DOH for reporting purposes.
"",
breakOnError =
// boolean; instructs doh to call the debugger upon a test failures; this can be helpful when
// trying to isolate exactly where the test failed
false,
async =
// boolean; config require.async==true before loading boot; this will have the effect of making
// version 1.7+ dojo bootstrap/loader operating in async mode
false,
sandbox =
// boolean; use a loader configuration that sandboxes the dojo and dojox objects used by doh
false,
trim = function(text){
if(text instanceof Array){
for (var result= [], i= 0; i<text.length; i++) {
result.push(trim(text[i]));
}
return result;
}else{
return text.match(/[^\s]*/)[0];
}
};
qstr = window.location.search.substr(1);
if(qstr.length){
for(var qparts = qstr.split("&"), x = 0; x < qparts.length; x++){
var tp = qparts[x].split("="), name=tp[0], value=(tp[1]||"").replace(/[<>"':\(\)]/g, ""); // replace() to avoid XSS attack
//Avoid URLs that use the same protocol but on other domains, for security reasons.
if (value.indexOf("//") === 0 || value.indexOf("\\\\") === 0) {
throw "Insupported URL";
}
switch(name){
// Note:
// * dojoUrl is deprecated, and is a synonym for boot
// * testUrl is deprecated, and is a synonym for test
// * testModule is deprecated, and is a synonym for test (dots are automatically replaced with slashes)
// * registerModulePath is deprecated, and is a synonym for paths
case "boot":
case "dojoUrl":
boot= trim(value.split(","));
break;
case "test":
case "testUrl":
test= trim(value.split(","));
break;
case "testModule":
test= trim(value.replace(/\./g, "/").split(","));
break;
// registerModulePath is deprecated; use "paths"
case "registerModulePath":
case "paths":
for(var path, modules = value.split(";"), i= 0; i<modules.length; i++){
path= modules[i].split(",");
paths[trim(path[0])]= trim(path[1]);
}
break;
case "breakOnError":
breakOnError= true;
break;
case "sandbox":
sandbox= true;
break;
case "async":
async= true;
break;
case "dohPlugins":
dohPlugins=value.split(";");
break;
}
}
}
var config;
if(sandbox){
// configure the loader assuming the dojo loader; of course the injected boot(s) can override this config
config= {
paths: paths,
// this config uses the dojo loader's scoping features to sandbox the version of dojo used by doh
packages: [{
name: 'doh',
location: '../util/doh',
// here's the magic...every time doh asks for a "dojo" module, it gets mapped to a "dohDojo"
// module; same goes for dojox/dohDojox since doh uses dojox
packageMap: {dojo:"dohDojo", dojox:"dohDojox"}
},{
// now define the dohDojo package...
name: 'dohDojo',
location: '../dojo',
packageMap: {dojo: "dohDojo", dojox: "dohDojox"}
},{
// and the dohDojox package...
name: 'dohDojox',
location: '../dojox',
// and dojox uses dojo...that is, dohDojox...which must be mapped to dohDojo in the context of dohDojox
packageMap: {dojo: "dohDojo", dojox: "dohDojox"}
}],
// next, we need to preposition a special configuration for dohDojo
cache: {
"dohDojo*_base/config": function(){
define([], {
// this configuration keeps dojo, dijit, and dojox out of the global space
scopeMap: [["dojo", "dohDojo"], ["dijit", "dohDijit"], ["dojox", "dohDojox"]],
isDebug: true,
noGlobals: true
});
}
},
// control the loader; don't boot global dojo, doh will ask for dojo itself
has: {
"dojo-sniff": 0,
"dojo-loader": 1,
"dojo-boot": 0,
"dojo-test-sniff": 1
},
// no sniffing; therefore, set the baseUrl
baseUrl: "../../dojo",
deps: ["dohDojo/domReady", "doh"],
callback: function(domReady, doh){
domReady(function(){
doh._fixHeight();
doh.breakOnError= breakOnError;
require(test, function(){
doh.run();
});
});
},
async: async
};
}else{
config= {
paths: paths,
deps: ["dojo/domReady", "doh"],
callback: function(domReady, doh){
domReady(function(){
doh._fixHeight();
doh.breakOnError= breakOnError;
require(test, function(){
doh.run();
});
});
},
async: async,
isDebug: 1
};
}
// load all of the dohPlugins
if(dohPlugins){
var i = 0;
for(i = 0; i < dohPlugins.length; i++){
config.deps.push(dohPlugins[i]);
}
}
require = config;
// now script inject any boots
for(var e, i = 0; i < boot.length; i++) {
if(boot[i]){
e = document.createElement("script");
e.type = "text/javascript";
e.src = boot[i];
e.charset = "utf-8";
document.getElementsByTagName("head")[0].appendChild(e);
}
}
})();
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 2 | define(["doh/runner", "require", "dojo/_base/config"], function(doh, require, config){ /*===== return { // summary: // Module for running DOH tests in rhino (as opposed to a browser). // Augments return value from doh/runner. }; =====*/ doh.debug= print; doh.error= print; // Override the doh._report method to make it quit with an // appropriate exit code in case of test failures. var oldReport = doh._report; doh._report = function(){ oldReport.apply(doh, arguments); if(this._failureCount > 0 || this._errorCount > 0){ quit(1); } }; print("\n"+doh._line); print("The Dojo Unit Test Harness, $Rev: e6490e5 $"); print("Copyright (c) 2011, The Dojo Foundation, All Rights Reserved"); for (var tests= [], args= config["commandLineArgs"], i= 0, arg; i<args.length; i++) { arg= (args[i]+"").split("="); if (arg.length==2 && arg[0]=="test") { var test= arg[1]; print("loading test " + test); tests.push(test); } } print(doh._line, "\n"); require(tests, function() { doh.run(); }); }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 1 1 | var testResourceRe = /^doh\/tests/,
list = {
"doh/doh.profile": 1,
"doh/package.json": 1,
"doh/tests": 1,
"doh/_parseURLargs": 1
},
copyOnly = function(mid){
return (mid in list);
};
var profile = {
resourceTags: {
test: function(filename, mid){
return testResourceRe.test(mid);
},
copyOnly: function(filename, mid){
return copyOnly(mid);
},
amd: function(filename, mid){
return !testResourceRe.test(mid) && !copyOnly(mid) && /\.js$/.test(filename);
}
}
};
|
| 1 2 3 4 5 6 7 8 9 | 2 | define([ "doh/runner", "dojo/has!host-browser?doh/_browserRunner", "dojo/has!host-node?doh/_nodeRunner", "dojo/has!host-rhino?doh/_rhinoRunner"], function(doh) { return doh; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 | 2 1 1 1 | define([ "doh/_browserRunner", "require", "dojo/aspect", "dojo/Deferred", "dojo/dom-class", "dojo/dom-construct", "dojo/dom-geometry", "dojo/_base/lang", "dojo/ready", "dojo/_base/unload", "dojo/when", "dojo/_base/window", "dojo/sniff", "dojo/has", "dojo/has!android?doh/plugins/android-webdriver-robot" ], function(doh, require, aspect, Deferred, domClass, construct, geom, lang, ready, unload, when, win, sniff, has, webdriver){ // loading state var _robot = null; var isSecure = (function(){ var key = Math.random(); return function(fcn){ return key; }; })(); var _keyPress = function(/*Number*/ charCode, /*Number*/ keyCode, /*Boolean*/ alt, /*Boolean*/ ctrl, /*Boolean*/ shift, /*Boolean*/ meta, /*Integer?*/ delay, /*Boolean*/ async){ // internal function to type one non-modifier key // typecasting Numbers helps Sun's IE plugin lookup methods that take int arguments // otherwise JS will send a double and Sun will complain _robot.typeKey(isSecure(), Number(charCode), Number(keyCode), Boolean(alt), Boolean(ctrl), Boolean(shift), Boolean(meta), Number(delay||0), Boolean(async||false)); }; // Queue of pending actions plus the currently executing action registered via sequence(). // Each action is a function that either: // 1. does a setTimeout() // 2. calls java Robot (mouse movement, typing a single letter, etc.) // 3. executes user defined function (for when app called sequence() directly). // Each function can return a Promise, or just a plain value if it executes synchronously. var seqPromise; aspect.before(doh, "_runFixture", function(){ // At the start of each new test fixture, clear any leftover queued actions from the previous test fixture. // This will happen when the previous test throws an error, or times out. var _seqPromise = seqPromise; // need setTimeout to avoid false error; seqPromise from passing test is not fulfilled until after this execution trace finishes! // really we should not have both `seqPromise` here and `var d = new doh.Deferred()` in the test setTimeout(function(){ if(_seqPromise && !_seqPromise.isFulfilled()){ _seqPromise.cancel(new Error("new test starting, cancelling pending & in-progress queued events from previous test")); } },0); seqPromise = new Deferred(); seqPromise.resolve(true); }); // Previous mouse position (from most recent mouseMoveTo() command) var lastMouse = {x: 5, y: 5}; // For 2.0, remove code to set doh.robot global. var robot = doh.robot = { _robotLoaded: true, _robotInitialized: false, // prime the event pump for fast browsers like Google Chrome - it's so fast, it doesn't stop to listen for keypresses! _spaceReceived: false, _primePump: false, _killApplet: function(){}, // overridden by Robot.html killRobot: function(){ if(robot._robotLoaded){ robot._robotLoaded = false; domClass.remove(document.documentElement, "dohRobot"); robot._killApplet(); } }, // Robot init methods // controls access to doh.run // basically, doh.run takes two calls to start the robot: // one (or more after the robot loads) from the test page // one from either the applet or an error condition _runsemaphore: { lock: ["lock"], unlock: function(){ try{ return this.lock.shift(); }catch(e){ return null; } } }, startRobot: function(){ //startRobot should be called to initialize the robot (after the java applet is loaded). //one good place to do this is in a dojo.addOnLoad handler. This function will be called //automatically if it is not already called when doh.run() is invoked. if(!this._robotInitialized){ this._robotInitialized = true; // if the iframe requested the applet and got a 404, then _robot is obviously unavailable // at least run the non-robot tests! if(robot._appletDead){ robot._onKeyboard(); }else{ _robot._callLoaded(isSecure()); } } // When robot finishes initializing it types a key, firing the _onKeyboard() listener, which calls _run(), // which resolves this Deferred. return this._started; }, // _loaded: Deferred // Deferred that resolves when the _initRobot() has been called. // Note to be confused with dojo/robotx.js, which defines initRobot() without an underscore _loaded: new doh.Deferred(), _initRobot: function(r){ // called from Robot // Robot calls _initRobot in its startup sequence // Prevent rerunning the whole test (see #8958 for details) if(doh._initRobotCalled){ return; } doh._initRobotCalled = true; // add dohRobot class to HTML element so tests can use that in CSS rules if desired domClass.add(document.documentElement, "dohRobot"); window.scrollTo(0, 0); // document.documentElement.scrollTop = document.documentElement.scrollLeft = 0; _robot = r; _robot._setKey(isSecure()); this._loaded.resolve(true); }, // _started: Deferred // Deferred that resolves when startRobot() has signaled completing by typing on the keyboard, // which in turn calls _run(). _started: new doh.Deferred(), // some utility functions to help the iframe use private variables _run: function(frame){ // called after the robot has been able to type on the keyboard, indicating that it's started frame.style.visibility = "hidden"; this._started.resolve(true); }, _initKeyboard: function(){ _robot._initKeyboard(isSecure()); }, _onKeyboard: function(){ // replaced by iframe when applet present. // remote robots don't have frames so pass a mock frame. this._run({style:{visibility:""}}); }, _initWheel: function(){ _robot._initWheel(isSecure()); }, _setDocumentBounds: function(docScreenX, docScreenY){ var robotView = document.getElementById("dohrobotview"); _robot.setDocumentBounds(isSecure(), Number(docScreenX), Number(docScreenY), Number(robotView.offsetLeft), Number(robotView.offsetTop)); }, _notified: function(keystring){ _robot._notified(isSecure(), keystring); }, // if the applet is 404 or cert is denied, this becomes true and kills tests _appletDead: false, _assertRobot: function(){ // make sure the applet is there and cert accepted // otherwise, skip the test requesting the robot action if(robot._appletDead){ throw new Error('robot not available; skipping test.'); } }, _mouseMove: function(/*Number*/ x, /*Number*/ y, /*Boolean*/ absolute, /*Integer?*/ duration){ // This function is no longer used, but left for back-compat if(absolute){ var scroll = {y: (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0), x: (window.pageXOffset || geom.fixIeBiDiScrollLeft(document.documentElement.scrollLeft) || document.body.scrollLeft || 0)}; y -= scroll.y; x -= scroll.x; } _robot.moveMouse(isSecure(), Number(x), Number(y), Number(0), Number(duration||100)); }, // Main robot API sequence: function(/*Function*/ f, /*Integer?*/ delay, /*Integer?*/ duration){ // summary: // Defer an action by adding it to the robot's incrementally delayed queue of actions to execute. // f: // A function containing actions you want to defer. It can return a Promise // to delay further actions. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all // duration: // Delay to wait after firing. function waitFunc(ms){ // Returns a function that returns a Promise that fires after ms milliseconds. return function(){ var timer, d; d = new Deferred(function(){ clearTimeout(timer); }); timer = setTimeout(function(){ d.resolve(true); }, ms); return d; }; } // Queue action to run specified function, plus optional "wait" actions for delay and duration. if(delay){ seqPromise = seqPromise.then(waitFunc(delay)); } seqPromise = seqPromise.then(f); if(duration){ seqPromise = seqPromise.then(waitFunc(duration)); } }, typeKeys: function(/*String|Number*/ chars, /*Integer?*/ delay, /*Integer?*/ duration){ // summary: // Types a string of characters in order, or types a dojo.keys.* constant. // description: // Types a string of characters in order, or types a dojo.keys.* constant. // example: // | robot.typeKeys("dijit.ed", 500); // chars: // String of characters to type, or a dojo.keys.* constant // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all // duration: // Time, in milliseconds, to spend pressing all of the keys. // The default is (string length)*50 ms. this._assertRobot(); var isNum = typeof(chars) == Number; duration = duration||(isNum?50: chars.length*50); if(isNum){ this.sequence(lang.partial(_keyPress, chars, chars, false, false, false, false, 0, 0), delay, duration); }else{ for(var i = 0; i < chars.length; i++){ this.sequence(lang.partial(_keyPress, chars.charCodeAt(i), 0, false, false, false, false, 0, 0), i == 0 ? delay : 0, Math.max(Math.ceil(duration/chars.length), 0)); } } }, keyPress: function(/*Integer*/ charOrCode, /*Integer?*/ delay, /*Object*/ modifiers, /*Boolean*/ asynchronous){ // summary: // Types a key combination, like SHIFT-TAB. // description: // Types a key combination, like SHIFT-TAB. // example: // to press shift-tab immediately, call robot.keyPress(dojo.keys.TAB, 0, {shift: true}) // charOrCode: // char/JS keyCode/dojo.keys.* constant for the key you want to press // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all // modifiers: // JSON object that represents all of the modifier keys being pressed. // It takes the following Boolean attributes: // // - shift // - alt // - ctrl // - meta // asynchronous: // If true, the delay happens asynchronously and immediately, outside of the browser's JavaScript thread and any previous calls. // This is useful for interacting with the browser's modal dialogs. this._assertRobot(); if(!modifiers){ modifiers = {alt:false, ctrl:false, shift:false, meta:false}; }else{ // normalize modifiers var attrs = ["alt", "ctrl", "shift", "meta"]; for(var i = 0; i<attrs.length; i++){ if(!modifiers[attrs[i]]){ modifiers[attrs[i]] = false; } } } var isChar = typeof(charOrCode)=="string"; if(asynchronous){ _keyPress(isChar?charOrCode.charCodeAt(0):0, isChar?0:charOrCode, modifiers.alt, modifiers.ctrl, modifiers.shift, modifiers.meta, delay, true); return; } this.sequence(function(){ _keyPress(isChar?charOrCode.charCodeAt(0):0, isChar?0:charOrCode, modifiers.alt, modifiers.ctrl, modifiers.shift, modifiers.meta, 0); }, delay); }, keyDown: function(/*Integer*/ charOrCode, /*Integer?*/ delay){ // summary: // Holds down a single key, like SHIFT or 'a'. // description: // Holds down a single key, like SHIFT or 'a'. // example: // to hold down the 'a' key immediately, call robot.keyDown('a') // charOrCode: // char/JS keyCode/dojo.keys.* constant for the key you want to hold down // Warning: holding down a shifted key, like 'A', can have unpredictable results. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all this._assertRobot(); this.sequence(function(){ var isChar = typeof(charOrCode)=="string"; _robot.downKey(isSecure(), isChar?charOrCode:0, isChar?0:charOrCode, 0); }, delay); }, keyUp: function(/*Integer*/ charOrCode, /*Integer?*/ delay){ // summary: // Releases a single key, like SHIFT or 'a'. // description: // Releases a single key, like SHIFT or 'a'. // example: // to release the 'a' key immediately, call robot.keyUp('a') // charOrCode: // char/JS keyCode/dojo.keys.* constant for the key you want to release // Warning: releasing a shifted key, like 'A', can have unpredictable results. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all this._assertRobot(); this.sequence(function(){ var isChar=typeof(charOrCode)=="string"; _robot.upKey(isSecure(), isChar?charOrCode:0, isChar?0:charOrCode, 0); }, delay); }, mouseClick: function(/*Object*/ buttons, /*Integer?*/ delay){ // summary: // Convenience function to do a press/release. // See robot.mousePress for more info. // description: // Convenience function to do a press/release. // See robot.mousePress for more info. this._assertRobot(); robot.mousePress(buttons, delay); robot.mouseRelease(buttons, 1); }, mousePress: function(/*Object*/ buttons, /*Integer?*/ delay){ // summary: // Presses mouse buttons. // description: // Presses the mouse buttons you pass as true. // Example: to press the left mouse button, pass {left: true}. // Mouse buttons you don't specify keep their previous pressed state. // buttons: // JSON object that represents all of the mouse buttons being pressed. // It takes the following Boolean attributes: // // - left // - middle // - right // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all this._assertRobot(); if(!buttons){ return; } this.sequence(function(){ var attrs = ["left", "middle", "right"]; for(var i = 0; i<attrs.length; i++){ if(!buttons[attrs[i]]){ buttons[attrs[i]] = false; } } _robot.pressMouse(isSecure(), Boolean(buttons.left), Boolean(buttons.middle), Boolean(buttons.right), Number(0)); }, delay); }, mouseMoveTo: function(/*Object*/ point, /*Integer?*/ delay, /*Integer?*/ duration, /*Boolean*/ absolute){ // summary: // Move the mouse from the current position to the specified point. // Delays reading contents point until queued command starts running. // See mouseMove() for details. // point: Object // x, y position relative to viewport, or if absolute == true, to document this._assertRobot(); duration = duration||100; // Calculate number of mouse movements we will do, based on specified duration. // IE6-8 timers have a granularity of 15ms, so only do one mouse move every 15ms var steps = duration<=1 ? 1 : // duration==1 -> user wants to jump the mouse (duration/15)|1; // |1 to ensure an odd # of intermediate steps for sensible interpolation var stepDuration = Math.floor(duration/steps); // Starting and ending points of the mouse movement. var start, end; this.sequence(function(){ // This runs right before we start moving the mouse. At this time (but not before), point is guaranteed // to be filled w/the correct data. So set start and end points for the movement of the mouse. start = lastMouse; if(absolute){ // Adjust end to be relative to viewport var scroll = {y: (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0), x: (window.pageXOffset || geom.fixIeBiDiScrollLeft(document.documentElement.scrollLeft) || document.body.scrollLeft || 0)}; end = { y: point.y - scroll.y, x: point.x - scroll.x }; }else{ end = point; } //console.log("mouseMoveTo() start, going from (", lastMouse.x, lastMouse.y, "), (", end.x, end.y, "), delay = " + // delay + ", duration = " + duration); }, delay || 0); // Function to positions the mouse along the line from start to end at the idx'th position (from 0 .. steps) function step(idx){ function easeInOutQuad(/*Number*/ t, /*Number*/ b, /*Number*/ c, /*Number*/ d){ t /= d / 2; if(t < 1) return Math.round(c / 2 * t * t + b); t--; return Math.round(-c / 2 * (t * (t - 2) - 1) + b); } var x = idx == steps ? end.x : easeInOutQuad(idx, start.x, end.x - start.x, steps), y = idx == steps ? end.y : easeInOutQuad(idx, start.y, end.y - start.y, steps); // If same position as the last time, don't bother calling java robot. if(x == lastMouse.x && y == lastMouse.y){ return true; } _robot.moveMouse(isSecure(), Number(x), Number(y), Number(0), Number(1)); lastMouse = {x: x, y: y}; } // Schedule mouse moves from beginning to end of line. // Start from t=1 because there's no need to move the mouse to where it already is for (var t = 1; t <= steps; t++){ // Use lang.partial() to lock in value of t before the t++ this.sequence(lang.partial(step, t), 0, stepDuration); } }, mouseMove: function(/*Number*/ x, /*Number*/ y, /*Integer?*/ delay, /*Integer?*/ duration, /*Boolean*/ absolute){ // summary: // Moves the mouse to the specified x,y offset relative to the viewport. // x: // x offset relative to the viewport, in pixels, to move the mouse. // y: // y offset relative to the viewport, in pixels, to move the mouse. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left: true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all // duration: // Approximate time Robot will spend moving the mouse // The default is 100ms. This also affects how many mousemove events will // be generated, which is the log of the duration. // absolute: // Boolean indicating whether the x and y values are absolute coordinates. // If false, then mouseMove expects that the x,y will be relative to the window. (clientX/Y) // If true, then mouseMove expects that the x,y will be absolute. (pageX/Y) this.mouseMoveTo({x: x, y: y}, delay, duration, absolute); }, mouseRelease: function(/*Object*/ buttons, /*Integer?*/ delay){ // summary: // Releases mouse buttons. // description: // Releases the mouse buttons you pass as true. // Example: to release the left mouse button, pass {left: true}. // Mouse buttons you don't specify keep their previous pressed state. // See robot.mousePress for more info. this._assertRobot(); if(!buttons){ return; } this.sequence(function(){ var attrs = ["left", "middle", "right"]; for(var i = 0; i<attrs.length; i++){ if(!buttons[attrs[i]]){ buttons[attrs[i]] = false; } } _robot.releaseMouse(isSecure(), Boolean(buttons.left), Boolean(buttons.middle), Boolean(buttons.right), Number(0)); }, delay); }, // mouseWheelSize: Integer value that determines the amount of wheel motion per unit mouseWheelSize: 1, mouseWheel: function(/*Number*/ wheelAmt, /*Integer?*/ delay, /*Integer?*/ duration){ // summary: // Spins the mouse wheel. // description: // Spins the wheel wheelAmt "notches." // Negative wheelAmt scrolls up/away from the user. // Positive wheelAmt scrolls down/toward the user. // Note: this will all happen in one event. // Warning: the size of one mouse wheel notch is an OS setting. // You can access this size from robot.mouseWheelSize // wheelAmt: // Number of notches to spin the wheel. // Negative wheelAmt scrolls up/away from the user. // Positive wheelAmt scrolls down/toward the user. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // robot.mouseClick({left: true}, 100) // first call; wait 100ms // robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all // duration: // Approximate time Robot will spend moving the mouse // By default, the Robot will wheel the mouse as fast as possible. this._assertRobot(); if(!wheelAmt){ return; } this.sequence(function(){ _robot.wheelMouse(isSecure(), Number(wheelAmt), Number(0), Number(duration||0)); }, delay, duration); }, setClipboard: function(/*String*/ data,/*String?*/ format){ // summary: // Set clipboard content. // description: // Set data as clipboard content, overriding anything already there. The // data will be put to the clipboard using the given format. // data: // New clipboard content to set // format: // Set this to "text/html" to put richtext to the clipboard. // Otherwise, data is treated as plaintext. By default, plaintext // is used. if(format==='text/html'){ _robot.setClipboardHtml(isSecure(), data); }else{ _robot.setClipboardText(isSecure(), data); } } }; // After page has finished loading, create the applet iframe. // Note: could eliminate dojo/ready dependency by tying this code to startRobot() call, but then users // are required to put doh.run() inside of a dojo/ready. Probably they are already doing that though. ready(function(){ // console.log("creating applet iframe"); var iframesrc; var scripts = document.getElementsByTagName("script"); for(var x = 0; x<scripts.length; x++){ var s = scripts[x].getAttribute('src'); if(s && (s.substr(s.length-9) == "runner.js")){ iframesrc = s.substr(0, s.length-9)+'Robot.html'; break; } } if(!iframesrc){ // if user set document.domain to something else, send it to the Robot too iframesrc = require.toUrl("./Robot.html") + "?domain=" + escape(document.domain); } construct.place('<div id="dohrobotview" style="border:0px none; margin:0px; padding:0px; position:absolute; bottom:0px; right:0px; width:1px; height:1px; overflow:hidden; visibility:hidden; background-color:red;"></div>', win.body()); if(!has("doh-custom-robot")){ // load default robot when not custom def given construct.place('<iframe application="true" style="border:0px none; z-index:32767; padding:0px; margin:0px; position:absolute; left:0px; top:0px; height:100px; width:200px; overflow:hidden; background-color:transparent;" tabIndex="-1" src="'+iframesrc+'" ALLOWTRANSPARENCY="true"></iframe>', win.body()); }else{ // custom def given console.log("using custom robot"); _robot = webdriver; // mix in exports for(var i in _robot){ if(robot[i]&&_robot[i]){ robot[i]=_robot[i]; } } // continue init instead of waiting on frame robot._initRobot(_robot); } }); // Start the robot as the first "test" when DOH runs. doh.registerGroup("initialize robot", [ { name: "load robot", timeout: 20000, runTest: function(){ // first wait for robot to tell us it's loaded, i.e. that _initRobot() has been called return robot._loaded; } }, { name: "start robot", timeout: 20000, runTest: function(){ // then we call startRobot(), and wait it to asynchronously complete return robot.startRobot(); } } ]); // Register the killRobot() command as the last "test" to run. // There's no good API to do this, so instead call doh.registerGroup() when the app first calls doh.run(), // since presumably all the real tests have already been registered. Note that doh.run() is called multiple times, // so make sure to only call registerGroup() once. var _oldRun = doh.run; doh.run = function(){ doh.registerGroup("kill robot", { name: "killRobot", timeout: 10000, runTest: function(){ robot.killRobot(); } }); doh.run = _oldRun; doh.run(); }; return robot; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 | 2 1 1 | define("doh/runner", ["dojo/_base/lang"], function(lang){
var doh = {
// summary:
// Functions for registering and running automated tests.
};
// intentionally define global tests and global doh symbols
// TODO: scrub these globals from tests and remove this pollution for 2.0
tests = doh;
this.doh = doh;
doh._line = "------------------------------------------------------------";
doh.debug = function(){
// summary:
// takes any number of arguments and sends them to whatever debugging
// or logging facility is available in this environment
// YOUR TEST RUNNER NEEDS TO IMPLEMENT THIS
};
doh.error = function(){
// summary:
// logging method to be used to send Error objects, so that
// whatever debugging or logging facility you have can decide to treat it
// as an Error object and show additional information - such as stack trace
// YOUR TEST RUNNER NEEDS TO IMPLEMENT THIS
};
doh._AssertFailure = function(msg, hint){
if (doh.breakOnError) {
//>>excludeStart("debuggerCrashesRhino", /^shrinksafe.comments/.test(kwArgs.optimize));
debugger;
//>>excludeEnd("debuggerCrashesRhino")
}
if(!(this instanceof doh._AssertFailure)){
return new doh._AssertFailure(msg, hint);
}
if(hint){
msg = (new String(msg||""))+" with hint: \n\t\t"+(new String(hint)+"\n");
}
this.message = new String(msg||"");
return this;
};
doh._AssertFailure.prototype = new Error();
doh._AssertFailure.prototype.constructor = doh._AssertFailure;
doh._AssertFailure.prototype.name = "doh._AssertFailure";
doh.Deferred = function(canceller){
this.chain = [];
this.id = this._nextId();
this.fired = -1;
this.paused = 0;
this.results = [null, null];
this.canceller = canceller;
this.silentlyCancelled = false;
};
lang.extend(doh.Deferred, {
getTestErrback: function(cb, scope){
// summary:
// Replaces outer getTextCallback's in nested situations to avoid multiple callback(true)'s
var _this = this;
return function(){
try{
cb.apply(scope||doh.global||_this, arguments);
}catch(e){
_this.reject(e);
}
};
},
getTestCallback: function(cb, scope){
var _this = this;
return function(){
try{
cb.apply(scope||doh.global||_this, arguments);
}catch(e){
_this.reject(e);
return;
}
_this.resolve(true);
};
},
_nextId: (function(){
var n = 1;
return function(){ return n++; };
})(),
cancel: function(){
if(this.fired == -1){
if (this.canceller){
this.canceller(this);
}else{
this.silentlyCancelled = true;
}
if(this.fired == -1){
this.reject(new Error("Deferred(unfired)"));
}
}else if(this.fired == 0 && this.results[0] && this.results[0].cancel){
this.results[0].cancel();
}
},
_pause: function(){
this.paused++;
},
_unpause: function(){
this.paused--;
if ((this.paused == 0) && (this.fired >= 0)) {
this._fire();
}
},
_continue: function(res){
this._resback(res);
this._unpause();
},
_resback: function(res){
this.fired = ((res instanceof Error) ? 1 : 0);
this.results[this.fired] = res;
this._fire();
},
_check: function(){
if(this.fired != -1){
if(!this.silentlyCancelled){
throw new Error("already called!");
}
this.silentlyCancelled = false;
}
},
resolve: function(res){
this._check();
this._resback(res);
},
reject: function(res){
this._check();
if(!(res instanceof Error)){
res = new Error(res);
}
this._resback(res);
},
then: function(cb, eb){
this.chain.push([cb, eb]);
if(this.fired >= 0){
this._fire();
}
return this;
},
always: function(cb){
this.then(cb, cb);
},
otherwise: function(eb){
this.then(null, eb);
},
isFulfilled: function(){
return this.fired >= 0;
},
isResolved: function(){
return this.fired == 0;
},
isRejected: function(){
return this.fired == 1;
},
_fire: function(){
var chain = this.chain;
var fired = this.fired;
var res = this.results[fired];
var self = this;
var cb = null;
while(chain.length > 0 && this.paused == 0){
// Array
var pair = chain.shift();
var f = pair[fired];
if(f == null){
continue;
}
try {
res = f(res);
fired = ((res instanceof Error) ? 1 : 0);
if(res && res.then){
cb = function(res){
self._continue(res);
};
this._pause();
}
}catch(err){
fired = 1;
res = err;
}
}
this.fired = fired;
this.results[fired] = res;
if((cb)&&(this.paused)){
res.always(cb);
}
}
});
lang.extend(doh.Deferred, {
// Back compat methods, remove for 2.0
getFunctionFromArgs: function(){
// Like lang.hitch but first arg (context) is optional
var a = arguments;
if((a[0])&&(!a[1])){
if(typeof a[0] == "function"){
return a[0];
}else if(typeof a[0] == "string"){
return doh.global[a[0]];
}
}else if((a[0])&&(a[1])){
return lang.hitch(a[0], a[1]);
}
return null;
},
addCallbacks: function(cb, eb){
this.then(cb, eb);
},
addCallback: function(cb, cbfn){
var enclosed = this.getFunctionFromArgs(cb, cbfn);
if(arguments.length > 2){
enclosed = lang.hitch(null, enclosed, arguments, 2);
}
return this.then(enclosed);
},
addErrback: function(cb, cbfn){
var enclosed = this.getFunctionFromArgs(cb, cbfn);
if(arguments.length > 2){
enclosed = lang.hitch(null, enclosed, arguments, 2);
}
return this.otherwise(enclosed);
},
addBoth: function(cb, cbfn){
var enclosed = this.getFunctionFromArgs(cb, cbfn);
if(arguments.length > 2){
enclosed = lang.hitch(null, enclosed, arguments, 2);
}
return this.always(enclosed);
},
callback: function(val){
this.resolve(val);
},
errback: function(val){
this.reject(val);
}
});
//
// State Keeping and Reporting
//
doh._testCount = 0;
doh._groupCount = 0;
doh._errorCount = 0;
doh._failureCount = 0;
doh._currentGroup = null;
doh._currentTest = null;
doh._paused = true;
doh._init = function(){
this._currentGroup = null;
this._currentTest = null;
this._errorCount = 0;
this._failureCount = 0;
this.debug(this._testCount, "tests to run in", this._groupCount, "groups");
};
doh._groups = {};
//
// Test Types
//
doh._testTypes= {};
doh.registerTestType= function(name, initProc){
// summary:
// Adds a test type and associates a function used to initialize each test of the given type
// name: String
// The name of the type.
// initProc: Function
// Type specific test initializer; called after the test object is created.
doh._testTypes[name]= initProc;
};
doh.registerTestType("perf", function(group, tObj, type){
// Augment the test with some specific options to make it identifiable as a
// particular type of test so it can be executed properly.
if(type === "perf" || tObj.testType === "perf"){
tObj.testType = "perf";
// Build an object on the root DOH class to contain all the test results.
// Cache it on the test object for quick lookup later for results storage.
if(!doh.perfTestResults){
doh.perfTestResults = {};
doh.perfTestResults[group] = {};
}
if(!doh.perfTestResults[group]){
doh.perfTestResults[group] = {};
}
if(!doh.perfTestResults[group][tObj.name]){
doh.perfTestResults[group][tObj.name] = {};
}
tObj.results = doh.perfTestResults[group][tObj.name];
// If it's not set, then set the trial duration; default to 100ms.
if(!("trialDuration" in tObj)){
tObj.trialDuration = 100;
}
// If it's not set, then set the delay between trial runs to 100ms
// default to 100ms to allow for GC and to make IE happy.
if(!("trialDelay" in tObj)){
tObj.trialDelay = 100;
}
// If it's not set, then set number of times a trial is run to 10.
if(!("trialIterations" in tObj)){
tObj.trialIterations = 10;
}
}
});
//
// Test Registration
//
var
createFixture= function(group, test, type){
// test is a function, string, or fixture object
var tObj = test;
if(lang.isString(test)){
tObj = {
name: test.replace("/\s/g", "_"), // FIXME: bad escapement
runTest: new Function("t", test)
};
}else if(lang.isFunction(test)){
// if we didn't get a fixture, wrap the function
tObj = { "runTest": test };
if(test["name"]){
tObj.name = test.name;
}else{
try{
var fStr = "function ";
var ts = tObj.runTest+"";
if(0 <= ts.indexOf(fStr)){
tObj.name = ts.split(fStr)[1].split("(", 1)[0];
}
// doh.debug(tObj.runTest.toSource());
}catch(e){
}
}
// FIXME: try harder to get the test name here
}else if(lang.isString(tObj.runTest)){
tObj.runTest= new Function("t", tObj.runTest);
}
if(!tObj.runTest){
return 0;
}
// if the test is designated as a particular type, do type-specific initialization
var testType= doh._testTypes[type] || doh._testTypes[tObj.testType];
if(testType){
testType(group, tObj);
}
// add the test to this group
doh._groups[group].push(tObj);
doh._testCount++;
doh._testRegistered(group, tObj);
return tObj;
},
dumpArg= function(arg){
if(lang.isString(arg)){
return "string(" + arg + ")";
} else {
return typeof arg;
}
},
illegalRegister= function(args, testArgPosition){
var hint= "\targuments: ";
for(var i= 0; i<5; i++){
hint+= dumpArg(args[i]);
}
doh.debug("ERROR:");
if(testArgPosition){
doh.debug("\tillegal arguments provided to doh.register; the test at argument " + testArgPosition + " wasn't a test.");
}else{
doh.debug("\tillegal arguments provided to doh.register");
}
doh.debug(hint);
};
doh._testRegistered = function(group, fixture){
// slot to be filled in
};
doh._groupStarted = function(group){
// slot to be filled in
};
doh._groupFinished = function(group, success){
// slot to be filled in
};
doh._testStarted = function(group, fixture){
// slot to be filled in
};
doh._testFinished = function(group, fixture, success){
// slot to be filled in
};
doh._registerTest = function(group, test, type){
// summary:
// add the provided test function or fixture object to the specified
// test group.
// group: String
// string name of the group to add the test to
// test: Function||String||Object
// TODOC
// type: String?
// An identifier denoting the type of testing that the test performs, such
// as a performance test. If falsy, defaults to test.type.
// get, possibly create, the group object
var groupObj = this._groups[group];
if(!groupObj){
this._groupCount++;
groupObj = this._groups[group] = [];
groupObj.inFlight = 0;
}
if(!test){
return groupObj;
}
// create the test fixture
var tObj;
if(lang.isFunction(test) || lang.isString(test) || "runTest" in test){
return createFixture(group, test, type) ? groupObj : 0;
}else if(lang.isArray(test)){
// a vector of tests...
for(var i=0; i<test.length; i++){
tObj = createFixture(group, test[i], type);
if(!tObj){
this.debug("ERROR:");
this.debug("\tillegal test is test array; more information follows...");
return null;
}
}
return groupObj;
}else{
// a hash of tests...
for(var testName in test){
var theTest = test[testName];
if(lang.isFunction(theTest) || lang.isString(theTest)){
tObj = createFixture(group, {name: testName, runTest: theTest}, type);
}else{
// should be an object
theTest.name = theTest.name || testName;
tObj = createFixture(group, theTest, type);
}
if(!tObj){
this.debug("ERROR:");
this.debug("\tillegal test is test hash; more information follows...");
return null;
}
}
return groupObj;
}
};
doh._registerTestAndCheck = function(groupId, test, type, testArgPosition, args, setUp, tearDown){
var amdMid = 0;
if(groupId){
if(type){
// explicitly provided type; therefore don't try to get type from groupId
var match = groupId.match(/([^\!]+)\!(.+)/);
if(match){
amdMid = match[1];
groupId = match[2];
}
}else{
var parts = groupId && groupId.split("!");
if(parts.length == 3){
amdMid = parts[0];
groupId = parts[1];
type = parts[2];
}else if(parts.length == 2){
// either (amdMid, group) or (group, type)
if(parts[1] in doh._testTypes){
groupId = parts[0];
type = parts[1];
}else{
amdMid = parts[0];
groupId = parts[1];
}
} // else, no ! and just a groupId
}
}
var group = doh._registerTest(groupId, test, type);
if(group){
if(amdMid){
group.amdMid = amdMid;
}
if(setUp){
group.setUp = setUp;
}
if(tearDown){
group.tearDown = tearDown;
}
}else{
illegalRegister(arguments, testArgPosition);
}
};
doh._registerUrl = function(/*String*/ group, /*String*/ url, /*Integer*/ timeout, /*String*/ type, /*object*/ dohArgs){
// slot to be filled in
this.debug("ERROR:");
this.debug("\tNO registerUrl() METHOD AVAILABLE.");
};
var typeSigs = (function(){
// Generate machinery to decode the many register signatures; these are the possible signatures.
var sigs = [
// note: to===timeout, up===setUp, down===tearDown
// 1 arg
"test", function(args, a1){doh._registerTestAndCheck("ungrouped", a1, 0, 0, args, 0, 0);},
"url", function(args, a1){doh._registerUrl("ungrouped", a1);},
// 2 args
"group-test", function(args, a1, a2){doh._registerTestAndCheck(a1, a2, 0, 0, args, 0, 0);},
"test-type", function(args, a1, a2){doh._registerTestAndCheck("ungrouped", a1, a2, 1, args, 0, 0);},
"test-up", function(args, a1, a2){doh._registerTestAndCheck("ungrouped", a1, 0, 0, args, a2, 0);},
"group-url", function(args, a1, a2){doh._registerUrl(a1, a2);},
"url-to", function(args, a1, a2){doh._registerUrl("ungrouped", a1, a2);},
"url-type", function(args, a1, a2){doh._registerUrl("ungrouped", a1, undefined, a2);},
"url-args", function(args, a1, a2){doh._registerUrl("ungrouped", a1, undefined, 0, a2);},
// 3 args
"group-test-type", function(args, a1, a2, a3){doh._registerTestAndCheck(a1, a2, a3, 2, args, 0, 0);},
"group-test-up", function(args, a1, a2, a3){doh._registerTestAndCheck(a1, a2, 0, 2, args, a3, 0);},
"test-type-up", function(args, a1, a2, a3){doh._registerTestAndCheck("ungrouped", a1, a2, 0, args, a3, 0);},
"test-up-down", function(args, a1, a2, a3){doh._registerTestAndCheck("ungrouped", a1, 0, 0, args, a2, a3);},
"group-url-to", function(args, a1, a2, a3){doh._registerUrl(a1, a2, a3);},
"group-url-type", function(args, a1, a2, a3){doh._registerUrl(a1, a2, undefined, a3);},
"group-url-args", function(args, a1, a2, a3){doh._registerUrl(a1, a2, undefined, 0, a3);},
"url-to-type", function(args, a1, a2, a3){doh._registerUrl("ungrouped", a1, a2, a3);},
"url-to-args", function(args, a1, a2, a3){doh._registerUrl("ungrouped", a1, a2, 0, a3);},
"url-type-args", function(args, a1, a2, a3){doh._registerUrl("ungrouped", a1, undefined, a2, a3);},
// 4 args
"group-test-type-up", function(args, a1, a2, a3, a4){doh._registerTestAndCheck(a1, a2, a3, 2, args, a4, 0);},
"group-test-up-down", function(args, a1, a2, a3, a4){doh._registerTestAndCheck(a1, a2, 0, 2, args, a3, a4);},
"test-type-up-down", function(args, a1, a2, a3, a4){doh._registerTestAndCheck("ungrouped", a1, 2, 0, args, a3, a4);},
"group-url-to-type", function(args, a1, a2, a3, a4){doh._registerUrl(a1, a2, a3, a4);},
"group-url-to-args", function(args, a1, a2, a3, a4){doh._registerUrl(a1, a2, a3, 0, a4);},
"group-url-type-args", function(args, a1, a2, a3, a4){doh._registerUrl(a1, a2, undefined, a3, a4);},
"url-to-type-args", function(args, a1, a2, a3, a4){doh._registerUrl("ungrouped", a1, a2, a3, a4);},
// 5 args
"group-test-type-up-down", function(args, a1, a2, a3, a4, a5){doh._registerTestAndCheck(a1, a2, a3, 2, args, a4, a5);},
"group-url-to-type-args", function(args, a1, a2, a3, a4, a5){doh._registerUrl(a1, a2, a3, a4, a5);}
];
// type-ids
// a - array
// st - string, possible type
// sf - string, possible function
// s - string not a type or function
// o - object
// f - function
// n - number
// see getTypeId inside doh.register
var argTypes = {
group:"st.sf.s",
test:"a.sf.o.f",
type:"st",
up:"f",
down:"f",
url:"s",
to:"n",
args:"o"
};
for(var p in argTypes){
argTypes[p]= argTypes[p].split(".");
}
function generateTypeSignature(sig, pattern, dest, func){
for(var nextPattern, reducedSig= sig.slice(1), typeList= argTypes[sig[0]], i=0; i<typeList.length; i++){
nextPattern = pattern + (pattern ? "-" : "") + typeList[i];
if(reducedSig.length){
generateTypeSignature(reducedSig, nextPattern, dest, func);
}else{
dest.push(nextPattern, func);
}
}
}
var typeSigs = [];
for(var sig, func, dest, i = 0; i<sigs.length; i++){
sig = sigs[i++].split("-");
func = sigs[i];
dest = typeSigs[sig.length-1] || (typeSigs[sig.length-1]= []);
generateTypeSignature(sig, "", dest, func);
}
return typeSigs;
})();
doh.register = function(a1, a2, a3, a4, a5){
/*=====
doh.register = function(groupId, testOrTests, timeoutOrSetUp, tearDown){
// summary:
// Add a test or group of tests.
// description:
// Adds the test or tests given by testsOrUrl to the group given by group (if any). For URL tests, unless
// a group is explicitly provided the group given by the URL until the document arrives at which
// point the group is renamed to the title of the document. For non-URL tests, if groupId is
// not provided, then tests are added to the group "ungrouped"; otherwise if the given groupId does not
// exist, it is created; otherwise, tests are added to the already-existing group.
//
// groupIds may contain embedded AMD module identifiers as prefixes and/or test types as suffixes. Prefixes
// and suffixes are denoted by a "!". For example
// groupId: String?
// The name of the group, optionally with an AMD module identifier prefix and/or
// test type suffix. The value character set for group names and AMD module indentifiers
// is given by [A-Za-z0-9_/.-]. If provided, prefix and suffix are denoted by "!". If
// provided, type must be a valid test type.
// testOrTests: Array||Function||Object||String||falsy
// When a function, implies a function that defines a single test. DOH passes the
// DOH object to the function as the sole argument when the test is executed. When
// a string, implies the definition of a single test given by `new Function("t", testOrTests)`.
// When an object that contains the method `runTest` (which *must* be a function),
// implies a single test given by the value of the property `runTest`. In this case,
// the object may also contain the methods `setup` and `tearDown`, and, if provided, these
// will be invoked on either side of the test function. Otherwise when an object (that is,
// an object that does not contain the method `runTest`), then a hash from test name to
// test function (either a function or string as described above); any names that begin
// with "_" are ignored. When an array, the array must exclusively contain functions,
// strings, and/or objects as described above and each item is added to the group as
// per the items semantics.
// timeoutOrSetUp: integer||Function?
// If tests is a URL, then must be an integer giving the number milliseconds to wait for the test
// page to load before signaling an error; otherwise, a function for initializing the test group.
// If a tearDown function is given, then a setup function must also be given.
// tearDown: Function?
// A function for deinitializing the test group.
// example:
// | `"myTest/MyGroup"` // just a group, group ids need not include a slash
// | `"myTest/MyGroup!perf"` // group with test type
// | `"path/to/amd/module!myTest/MyGroup"` // group with AMD module identifier
// | `"path/to/amd/module!myTest/MyGroup!perf"` // group with both AMD module identifier and test type
//
// Groups associated with AMD module identifiers may be unloaded/reloaded if using an AMD loader with
// reload support (dojo's AMD loader includes such support). If no AMD module identifier is given,
// the loader supports reloading, and the user demands a reload, then the groupId will be used
// as the AMD module identifier.
//
// For URL tests, the groupId is changed to the document title (if any) upon document arrival. The
// title may include a test type suffix denoted with a "!" as described above.
//
// For URL tests, if timeout is a number, then sets the timeout for loading
// the particular URL; otherwise, timeout is set to DOH.iframeTimeout.
//
// For non-URL tests, if setUp and/or tearDown are provided, then any previous setUp and/or
// tearDown functions for the group are replaced as given. You may affect just setUp and/or tearDown
// for a group and not provide/add any tests by providing falsy for the test argument.
// example:
// | var
// | t1= function(t) {
// | // this is a test
// | // t will be set to DOH when the test is executed by DOH
// | // etc.
// | },
// |
// | t2= {
// | // this is a test fixture and may be passed as a test
// |
// | // runTest is always required...
// | runTest: function(t){
// | // the test...
// | },
// |
// | // name is optional, but recommended...
// | name:"myTest",
// |
// | // preamble is optional...
// | setUp: function(){
// | // will be executed by DOH prior to executing the test
// | },
// |
// | // postscript is optional...
// | tearDown: function(){ // op
// | // will be executed by DOH after executing the test
// | }
// | }
// |
// | t3= [
// | // this is a vector of tests...
// | t1, t2
// | ],
// |
// | t4= {
// | // this is a map from test name to test or test fixture
// | t5: function(t){
// | // etc.
// | },
// |
// | t6: {
// | runTest: function(t){
// | // etc.
// | }
// | // name will be automatically added as "t6"
// | }
// | },
// |
// | aSetup: function(){
// | // etc.
// | },
// |
// | aTearDown: function(){
// | // etc.
// | };
// | // (test); note, can't provide setup/tearDown without a group
// | doh.register(t1);
// |
// | // (group, test, setUp, tearDown) test and/or setUp and/or tearDown can be missing
// | doh.register("myGroup", 0, aSetUp, aTearDown);
// | doh.register("myGroup", t1, aSetUp, aTearDown);
// | doh.register("myGroup", t1, aSetUp);
// | doh.register("myGroup", t1, 0, aTearDown);
// | doh.register("myGroup", t1);
// |
// | // various kinds of test arguments are allowed
// | doh.register("myGroup", t2);
// | doh.register("myGroup", t3);
// | doh.register("myGroup", t4);
// |
// | // add a perf test
// | doh.register("myGroup!perf", t1);
// |
// | // add a perf test with an AMD module identifier
// | doh.register("path/to/my/module!myGroup!perf", t1);
//
// doh.register also supports Dojo, v1.6- signature (group, test, type), although this signature is deprecated.
};
=====*/
function getTypeId(a){
if(a instanceof Array){
return "a";
}else if(typeof a == "function"){
return "f";
}else if(typeof a == "number"){
return "n";
}else if(typeof a == "string"){
if(a in doh._testTypes){
return "st";
}else if(/\(/.test(a)){
return "sf";
}else{
return "s";
}
}else{
return "o";
}
}
var
arity = arguments.length,
search = typeSigs[arity-1],
sig = [],
i;
for(i =0; i<arity; i++){
sig.push(getTypeId(arguments[i]));
}
sig = sig.join("-");
for(i=0; i<search.length; i+= 2){
if(search[i]==sig){
search[i+1](arguments, a1, a2, a3, a4, a5);
return;
}
}
illegalRegister(arguments);
};
doh.registerDocTests = function(module){
// summary:
// Deprecated. Won't work unless you manually load dojox.testing.DocTest, and likely not even then.
// Gets all the doctests from the given module and register each of them as a single test case here.
var docTest = new dojox.testing.DocTest();
var docTests = docTest.getTests(module);
var len = docTests.length;
var tests = [];
for (var i=0; i<len; i++){
var test = docTests[i];
// Extract comment on first line and add to test name.
var comment = "";
if (test.commands.length && test.commands[0].indexOf("//")!=-1) {
var parts = test.commands[0].split("//");
comment = ", "+parts[parts.length-1]; // Get all after the last //, so we don't get trapped by http:// or alikes :-).
}
tests.push({
runTest: (function(test){
return function(t){
var r = docTest.runTest(test.commands, test.expectedResult);
t.assertTrue(r.success);
};
})(test),
name:"Line "+test.line+comment
}
);
}
this.register("DocTests: "+module, tests);
};
//
// deprecated v1.6- register API follows
//
doh.registerTest = function(/*String*/ group, /*Array||Function||Object*/ test, /*String*/ type){
// summary:
// Deprecated. Use doh.register(group/type, test) instead
doh.register(group + (type ? "!" + type : ""), test);
};
doh.registerGroup = function(/*String*/ group, /*Array||Function||Object*/ tests, /*Function*/ setUp, /*Function*/ tearDown, /*String*/ type){
// summary:
// Deprecated. Use doh.register(group/type, tests, setUp, tearDown) instead
var args = [(group ? group : "") + (type ? "!" + type : ""), tests];
setUp && args.push(setUp);
tearDown && args.push(tearDown);
doh.register.apply(doh, args);
};
doh.registerTestNs = function(/*String*/ group, /*Object*/ ns){
// summary:
// Deprecated. Use doh.register(group, ns) instead
doh.register(group, ns);
};
doh.registerTests = function(/*String*/ group, /*Array*/ testArr, /*String*/ type){
// summary:
// Deprecated. Use doh.register(group/type, testArr) instead
doh.register(group + (type ? "!" + type : ""), testArr);
};
doh.registerUrl = function(/*String*/ group, /*String*/ url, /*Integer*/ timeout, /*String*/ type, /*Object*/ args){
// summary:
// Deprecated. Use doh.register(group/type, url, timeout) instead
doh.register(group + (type ? "!" + type : ""), url+"", timeout || 10000, args || {});
};
//
// Assertions and In-Test Utilities
//
doh.t = doh.assertTrue = function(/*Object*/ condition, /*String?*/ hint){
// summary:
// is the passed item "truthy"?
if(arguments.length < 1){
throw new doh._AssertFailure("assertTrue failed because it was not passed at least 1 argument");
}
//if(lang.isString(condition) && condition.length){
// return true;
//}
if(!eval(condition)){
throw new doh._AssertFailure("assertTrue('" + condition + "') failed", hint);
}
};
doh.f = doh.assertFalse = function(/*Object*/ condition, /*String?*/ hint){
// summary:
// is the passed item "falsey"?
if(arguments.length < 1){
throw new doh._AssertFailure("assertFalse failed because it was not passed at least 1 argument");
}
if(eval(condition)){
throw new doh._AssertFailure("assertFalse('" + condition + "') failed", hint);
}
};
doh.e = doh.assertError = function(/*Error object*/expectedError, /*Object*/scope, /*String*/functionName, /*Array*/args, /*String?*/ hint){
// summary:
// Test for a certain error to be thrown by the given function.
// example:
// t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]);
// t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]);
try{
scope[functionName].apply(scope, args);
}catch (e){
if(e instanceof expectedError){
return true;
}else{
throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut got\n\t\t"+e+"\n\n", hint);
}
}
throw new doh._AssertFailure("assertError() failed:\n\texpected error\n\t\t"+expectedError+"\n\tbut no error caught\n\n", hint);
};
doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual, /*String?*/ hint, doNotThrow){
// summary:
// are the passed expected and actual objects/values deeply
// equivalent?
// Compare undefined always with three equal signs, because undefined==null
// is true, but undefined===null is false.
if((expected === undefined)&&(actual === undefined)){
return true;
}
if(arguments.length < 2){
throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
}
if((expected === actual)||(expected == actual)||
( typeof expected == "number" && typeof actual == "number" && isNaN(expected) && isNaN(actual) )){
return true;
}
if( (lang.isArray(expected) && lang.isArray(actual))&&
(this._arrayEq(expected, actual)) ){
return true;
}
if( ((typeof expected == "object")&&((typeof actual == "object")))&&
(this._objPropEq(expected, actual)) ){
return true;
}
if (doNotThrow) {
return false;
}
throw new doh._AssertFailure("assertEqual() failed:\n\texpected\n\t\t"+expected+"\n\tbut got\n\t\t"+actual+"\n\n", hint);
};
doh.isNot = doh.assertNotEqual = function(/*Object*/ notExpected, /*Object*/ actual, /*String?*/ hint){
// summary:
// are the passed notexpected and actual objects/values deeply
// not equivalent?
// Compare undefined always with three equal signs, because undefined==null
// is true, but undefined===null is false.
if((notExpected === undefined)&&(actual === undefined)){
throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
}
if(arguments.length < 2){
throw doh._AssertFailure("assertEqual failed because it was not passed 2 arguments");
}
if((notExpected === actual)||(notExpected == actual)){
throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
}
if( (lang.isArray(notExpected) && lang.isArray(actual))&&
(this._arrayEq(notExpected, actual)) ){
throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
}
if( ((typeof notExpected == "object")&&((typeof actual == "object"))) ){
var isequal = false;
try{
isequal = this._objPropEq(notExpected, actual);
}catch(e){
if(!(e instanceof doh._AssertFailure)){
throw e; // other exceptions, just throw it
}
}
if(isequal){
throw new doh._AssertFailure("assertNotEqual() failed: not expected |"+notExpected+"| but got |"+actual+"|", hint);
}
}
return true;
};
doh._arrayEq = function(expected, actual){
if(expected.length != actual.length){ return false; }
// FIXME: we're not handling circular refs. Do we care?
for(var x=0; x<expected.length; x++){
if(!doh.assertEqual(expected[x], actual[x], 0, true)){ return false; }
}
return true;
};
doh._objPropEq = function(expected, actual){
// Degenerate case: if they are both null, then their "properties" are equal.
if(expected === null && actual === null){
return true;
}
// If only one is null, they aren't equal.
if(expected === null || actual === null){
return false;
}
if(expected instanceof Date){
return actual instanceof Date && expected.getTime()==actual.getTime();
}
var x;
// Make sure ALL THE SAME properties are in both objects!
for(x in actual){ // Lets check "actual" here, expected is checked below.
if(!(x in expected)){
return false;
}
}
for(x in expected){
if(!(x in actual)){
return false;
}
if(!doh.assertEqual(expected[x], actual[x], 0, true)){
return false;
}
}
return true;
};
//
// Runner-Wrapper
//
doh._setupGroupForRun = function(/*String*/ groupName){
var tg = this._groups[groupName];
this.debug(this._line);
this.debug("GROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run");
doh._groupStarted(groupName);
};
doh._handleFailure = function(groupName, fixture, e){
// this.debug("FAILED test:", fixture.name);
// mostly borrowed from JUM
this._groups[groupName].failures++;
var out = "";
if(e instanceof this._AssertFailure){
this._failureCount++;
if(e["fileName"]){ out += e.fileName + ':'; }
if(e["lineNumber"]){ out += e.lineNumber + ' '; }
out += e.message;
this.error("\t_AssertFailure:", out);
}else{
this._errorCount++;
this.error("\tError:", e.message || e); // printing Error on IE9 (and other browsers?) yields "[Object Error]"
}
if(fixture.runTest["toSource"]){
var ss = fixture.runTest.toSource();
this.debug("\tERROR IN:\n\t\t", ss);
}else{
this.debug("\tERROR IN:\n\t\t", fixture.runTest);
}
if(e.rhinoException){
e.rhinoException.printStackTrace();
}else if(e.javaException){
e.javaException.printStackTrace();
}
};
doh._runPerfFixture = function(/*String*/ groupName, /*Object*/ fixture){
// summary:
// This function handles how to execute a 'performance' test
// which is different from a straight UT style test. These
// will often do numerous iterations of the same operation and
// gather execution statistics about it, like max, min, average,
// etc. It makes use of the already in place DOH deferred test
// handling since it is a good idea to put a pause in between each
// iteration to allow for GC cleanup and the like.
// groupName:
// The test group that contains this performance test.
// fixture:
// The performance test fixture.
var tg = this._groups[groupName];
fixture.startTime = new Date();
// Perf tests always need to act in an async manner as there is a
// number of iterations to flow through.
var def = new doh.Deferred();
tg.inFlight++;
def.groupName = groupName;
def.fixture = fixture;
var threw = false;
def.otherwise(function(err){
doh._handleFailure(groupName, fixture, err);
threw = true;
});
// Set up the finalizer.
var fulfilled;
var retEnd = function(){
fulfilled = true;
if(fixture["tearDown"]){ fixture.tearDown(doh); }
tg.inFlight--;
if((!tg.inFlight)&&(tg.iterated)){
doh._groupFinished(groupName, !tg.failures);
}
doh._testFinished(groupName, fixture, !threw);
if(doh._paused){
doh.run();
}
};
// Since these can take who knows how long, we don't want to timeout
// unless explicitly set
var timer;
var to = fixture.timeout;
if(to > 0) {
timer = setTimeout(function(){
def.reject(new Error("test timeout in "+fixture.name.toString()));
}, to);
}
// Set up the end calls to the test into the deferred we'll return.
def.always(function(){
if(timer){
clearTimeout(timer);
}
retEnd();
});
// Okay, now set up the timing loop for the actual test.
// This is down as an async type test where there is a delay
// between each execution to allow for GC time, etc, so the GC
// has less impact on the tests.
var res = fixture.results;
res.trials = [];
// Try to figure out how many calls are needed to hit a particular threshold.
var itrDef = doh._calcTrialIterations(groupName, fixture);
// Blah, since tests can be deferred, the actual run has to be deferred until after
// we know how many iterations to run. This is just plain ugly.
itrDef.then(
function(iterations){
if(iterations){
var countdown = fixture.trialIterations;
doh.debug("TIMING TEST: [" + fixture.name +
"]\n\t\tITERATIONS PER TRIAL: " +
iterations + "\n\tTRIALS: " +
countdown);
// Figure out how many times we want to run our 'trial'.
// Where each trial consists of 'iterations' of the test.
var trialRunner = function() {
// Set up our function to execute a block of tests
var start = new Date();
var tTimer = new doh.Deferred();
var tState = {
countdown: iterations
};
var testRunner = function(state){
while(state){
try{
state.countdown--;
if(state.countdown){
var ret = fixture.runTest(doh);
if(ret && ret.then){
// Deferreds have to be handled async,
// otherwise we just keep looping.
var atState = {
countdown: state.countdown
};
ret.then(
function(){
testRunner(atState)
},
function(err){
doh._handleFailure(groupName, fixture, err);
fixture.endTime = new Date();
def.reject(err);
}
);
state = null;
}
}else{
tTimer.resolve(new Date());
state = null;
}
}catch(err){
fixture.endTime = new Date();
tTimer.reject(err);
}
}
};
tTimer.then(
function(end){
// Figure out the results and try to factor out function call costs.
var tResults = {
trial: (fixture.trialIterations - countdown),
testIterations: iterations,
executionTime: (end.getTime() - start.getTime()),
average: (end.getTime() - start.getTime())/iterations
};
res.trials.push(tResults);
doh.debug("\n\t\tTRIAL #: " +
tResults.trial + "\n\tTIME: " +
tResults.executionTime + "ms.\n\tAVG TEST TIME: " +
(tResults.executionTime/tResults.testIterations) + "ms.");
// Okay, have we run all the trials yet?
countdown--;
if(countdown){
setTimeout(trialRunner, fixture.trialDelay);
}else{
// Okay, we're done, let's compute some final performance results.
var t = res.trials;
// We're done.
fixture.endTime = new Date();
def.resolve(true);
}
},
// Handler if tTimer gets an error
function(err){
fixture.endTime = new Date();
def.reject(err);
}
);
testRunner(tState);
};
trialRunner();
}
},
// Handler if itrDef gets an error
function(err){
fixture.endTime = new Date();
def.reject(err);
}
);
// Set for a pause, returned the deferred.
if(!fulfilled){
doh.pause();
}
return def;
};
doh._calcTrialIterations = function(/*String*/ groupName, /*Object*/ fixture){
// summary:
// This function determines the rough number of iterations to
// use to reach a particular MS threshold. This returns a deferred
// since tests can theoretically by async. Async tests aren't going to
// give great perf #s, though.
// The callback is passed the # of iterations to hit the requested
// threshold.
// fixture:
// The test fixture we want to calculate iterations for.
var def = new doh.Deferred();
var calibrate = function () {
var testFunc = lang.hitch(fixture, fixture.runTest);
// Set the initial state. We have to do this as a loop instead
// of a recursive function. Otherwise, it blows the call stack
// on some browsers.
var iState = {
start: new Date(),
curIter: 0,
iterations: 5
};
var handleIteration = function(state){
while(state){
if(state.curIter < state.iterations){
try{
var ret = testFunc(doh);
if(ret && ret.then){
var aState = {
start: state.start,
curIter: state.curIter + 1,
iterations: state.iterations
};
ret.then(
function(){
handleIteration(aState);
},
function(err) {
fixture.endTime = new Date();
def.reject(err);
}
);
state = null;
}else{
state.curIter++;
}
}catch(err){
fixture.endTime = new Date();
def.reject(err);
return;
}
}else{
var end = new Date();
var totalTime = (end.getTime() - state.start.getTime());
if(totalTime < fixture.trialDuration){
var nState = {
iterations: state.iterations * 2,
curIter: 0
};
state = null;
setTimeout(function(){
nState.start = new Date();
handleIteration(nState);
}, 50);
}else{
var itrs = state.iterations;
setTimeout(function(){def.resolve(itrs)}, 50);
state = null;
}
}
}
};
handleIteration(iState);
};
setTimeout(calibrate, 10);
return def;
};
doh._runRegFixture = function(/*String*/ groupName, /*Object*/ fixture){
// summary:
// Function to help run a generic doh test. Called from _runFixture(). These are not
// specialized tests, like performance groups and such.
// groupName:
// The groupName of the test.
// fixture:
// The test fixture to execute.
var tg = this._groups[groupName];
fixture.startTime = new Date();
var ret = fixture.runTest(this);
// if we get a deferred back from the test runner, we know we're
// gonna wait for an async result. It's up to the test code to trap
// errors and give us an errback or callback.
if(ret && ret.then){
// If ret is a dojo/Deferred, get the corresponding Promise; it has some additional methods we need.
if(ret.promise){
ret = ret.promise;
}
tg.inFlight++;
ret.groupName = groupName;
ret.fixture = fixture;
// Setup handler for when test fails.
var threw = false;
ret.otherwise(function(err){
if(threw){
// the fixture timeout (below) must have already fired
return;
}
doh._handleFailure(groupName, fixture, err);
threw = true;
});
var fulfilled;
var retEnd = function(){
// summary:
// Called when tests finishes successfully, fails, or times out
if(fulfilled){
// retEnd() has already executed; probably the timeout above fired and then later ret completed.
return;
}
fulfilled = true;
fixture.endTime = new Date();
if(fixture.tearDown){
try {
fixture.tearDown(doh);
}catch(e){
this.debug("Error tearing down test: "+e.message);
}
}
tg.inFlight--;
doh._testFinished(groupName, fixture, !threw);
if((!tg.inFlight)&&(tg.iterated)){
doh._groupFinished(groupName, !tg.failures);
}
// Go on to next test
if(doh._paused){
doh.run();
}
};
var timer = setTimeout(function(){
if(!timer){
// we already called clearTimeout(), but it fired anyway, due to IE bug; just ignore.
return;
}
// Note: cannot call ret.reject() because ret may be a readonly promise
doh._handleFailure(groupName, fixture, new Error("test timeout in " + fixture.name.toString()));
threw = true;
retEnd();
}, fixture["timeout"]||100000);
ret.always(function(){
clearTimeout(timer);
timer = null;
retEnd();
});
if(!fulfilled){
doh.pause();
}
return ret;
}else{
// Synchronous test; tearDown etc. handled in _runFixture(), the function that called me
}
};
doh._runFixture = function(groupName, fixture){
var tg = this._groups[groupName];
this._testStarted(groupName, fixture);
var threw = false;
var err = null;
// run it, catching exceptions and reporting them
try{
// let doh reference "this.group.thinger..." which can be set by
// another test or group-level setUp function
fixture.group = tg;
// only execute the parts of the fixture we've got
if(fixture["setUp"]){ fixture.setUp(this); }
if(fixture["runTest"]){ // should we error out of a fixture doesn't have a runTest?
if(fixture.testType === "perf"){
// Always async deferred, so return it.
return doh._runPerfFixture(groupName, fixture);
}else{
// May or may not by async.
var ret = doh._runRegFixture(groupName, fixture);
if(ret){
// this design is ridiculous, but tearDown etc. is handled in _runRegFixture iff fixture is async;
// likewise with runPerfFixture
return ret;
}
}
}
}catch(e){
threw = true;
err = e;
}
// The rest of the code in this function executes only if test returns synchronously...
fixture.endTime = new Date();
// should try to tear down regardless whether test passed or failed...
try{
if(fixture["tearDown"]){ fixture.tearDown(this); }
}catch(e){
this.debug("Error tearing down test: "+e.message);
}
var d = new doh.Deferred();
setTimeout(lang.hitch(this, function(){
if(threw){
this._handleFailure(groupName, fixture, err);
}
this._testFinished(groupName, fixture, !threw);
if((!tg.inFlight)&&(tg.iterated)){
doh._groupFinished(groupName, !tg.failures);
}else if(tg.inFlight > 0){
setTimeout(lang.hitch(this, function(){
doh.runGroup(groupName);
}), 100);
this._paused = true;
}
if(doh._paused){
doh.run();
}
}), 30);
doh.pause();
return d;
};
doh.runGroup = function(/*String*/ groupName, /*Integer*/ idx){
// summary:
// runs the specified test group
// the general structure of the algorithm is to run through the group's
// list of doh, checking before and after each of them to see if we're in
// a paused state. This can be caused by the test returning a deferred or
// the user hitting the pause button. In either case, we want to halt
// execution of the test until something external to us restarts it. This
// means we need to pickle off enough state to pick up where we left off.
// FIXME: need to make fixture execution async!!
idx = idx || 0;
var tg = this._groups[groupName];
if(tg.skip === true){ return; }
if(lang.isArray(tg)){
if(tg.iterated===undefined){
tg.iterated = false;
tg.inFlight = 0;
tg.failures = 0;
this._setupGroupForRun(groupName);
if(tg["setUp"]){ tg.setUp(this); }
}
for(var y=idx; y<tg.length; y++){
if(this._paused){
this._currentTest = y;
// this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
return;
}
doh._runFixture(groupName, tg[y]);
if(this._paused){
this._currentTest = y+1;
if(this._currentTest == tg.length){ // RCG--don't think we need this; the next time through it will be taken care of
tg.iterated = true;
}
// this.debug("PAUSED at:", tg[y].name, this._currentGroup, this._currentTest);
return;
}
}
tg.iterated = true;
if(!tg.inFlight){
if(tg["tearDown"]){ tg.tearDown(this); }
doh._groupFinished(groupName, !tg.failures);
}
}
};
doh._onEnd = function(){};
doh._report = function(){
// summary:
// a private method to be implemented/replaced by the "locally
// appropriate" test runner
// this.debug("ERROR:");
// this.debug("\tNO REPORTING OUTPUT AVAILABLE.");
// this.debug("\tIMPLEMENT doh._report() IN YOUR TEST RUNNER");
this.debug(this._line);
this.debug("| TEST SUMMARY:");
this.debug(this._line);
this.debug("\t", this._testCount, "tests in", this._groupCount, "groups");
this.debug("\t", this._errorCount, "errors");
this.debug("\t", this._failureCount, "failures");
};
doh.togglePaused = function(){
this[(this._paused) ? "run" : "pause"]();
};
doh.pause = function(){
// summary:
// halt test run. Can be resumed.
this._paused = true;
};
doh.run = function(){
// summary:
// begins or resumes the test process.
this._paused = false;
var cg = this._currentGroup;
var ct = this._currentTest;
var found = false;
if(!cg){
this._init(); // we weren't paused
found = true;
}
this._currentGroup = null;
this._currentTest = null;
for(var x in this._groups){
if(
( (!found)&&(x == cg) )||( found )
){
if(this._paused){ return; }
this._currentGroup = x;
if(!found){
found = true;
this.runGroup(x, ct);
}else{
this.runGroup(x);
}
if(this._paused){ return; }
}
}
this._currentGroup = null;
this._currentTest = null;
this._paused = false;
this._onEnd();
this._report();
};
doh.runOnLoad = function(){
require(["dojo/ready"], function(ready){
ready(doh, "run");
});
};
return doh;
});
// backcompat hack: if in the browser, then loading doh/runner implies loading doh/_browserRunner. This is the
// behavior of 1.6- and is leveraged on many test documents that dojo.require("doh.runner"). Note that this
// hack will only work in synchronous mode; but if you're not in synchronous mode, you don't care about this.
// Remove for 2.0.
if (typeof window!="undefined" && typeof location!="undefined" && typeof document!="undefined" && window.location==location && window.document==document) {
require(["doh/_browserRunner"]);
}
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| AdapterRegistry.js | 5% | (1 / 20) | 0% | (0 / 12) | 0% | (0 / 5) | 5% | (1 / 20) | |
| Deferred.js | 0.88% | (1 / 113) | 0% | (0 / 74) | 0% | (0 / 19) | 0.88% | (1 / 113) | |
| DeferredList.js | 5.88% | (2 / 34) | 0% | (0 / 12) | 0% | (0 / 9) | 5.88% | (2 / 34) | |
| Evented.js | 20% | (2 / 10) | 100% | (0 / 0) | 0% | (0 / 5) | 20% | (2 / 10) | |
| NodeList-data.js | 2.86% | (1 / 35) | 0% | (0 / 19) | 0% | (0 / 6) | 3.13% | (1 / 32) | |
| NodeList-dom.js | 2.6% | (2 / 77) | 0% | (0 / 56) | 0% | (0 / 17) | 2.67% | (2 / 75) | |
| NodeList-fx.js | 4.76% | (1 / 21) | 0% | (0 / 12) | 0% | (0 / 11) | 4.76% | (1 / 21) | |
| NodeList-html.js | 10% | (1 / 10) | 0% | (0 / 2) | 0% | (0 / 3) | 10% | (1 / 10) | |
| NodeList-manipulate.js | 3.01% | (4 / 133) | 0% | (0 / 68) | 0% | (0 / 22) | 3.01% | (4 / 133) | |
| NodeList-traverse.js | 1.39% | (1 / 72) | 0% | (0 / 30) | 0% | (0 / 30) | 1.39% | (1 / 72) | |
| NodeList.js | 50% | (1 / 2) | 100% | (0 / 0) | 0% | (0 / 1) | 50% | (1 / 2) | |
| OpenAjax.js | 0.95% | (1 / 105) | 0% | (0 / 40) | 0% | (0 / 10) | 0.95% | (1 / 105) | |
| Stateful.js | 1.82% | (1 / 55) | 0% | (0 / 32) | 0% | (0 / 12) | 1.89% | (1 / 53) | |
| aspect.js | 4.41% | (3 / 68) | 0% | (0 / 44) | 0% | (0 / 10) | 4.41% | (3 / 68) | |
| back.js | 4.52% | (7 / 155) | 0% | (0 / 129) | 0% | (0 / 17) | 4.7% | (7 / 149) | |
| behavior.js | 5.36% | (3 / 56) | 0% | (0 / 26) | 0% | (0 / 15) | 5.56% | (3 / 54) | |
| cache.js | 50% | (1 / 2) | 100% | (0 / 0) | 0% | (0 / 1) | 50% | (1 / 2) | |
| colors.js | 2% | (1 / 50) | 0% | (0 / 38) | 0% | (0 / 7) | 2.33% | (1 / 43) | |
| cookie.js | 3.23% | (1 / 31) | 0% | (0 / 18) | 0% | (0 / 3) | 3.45% | (1 / 29) | |
| currency.js | 5% | (1 / 20) | 0% | (0 / 4) | 0% | (0 / 6) | 5% | (1 / 20) | |
| date.js | 0.69% | (1 / 145) | 0% | (0 / 92) | 0% | (0 / 7) | 0.7% | (1 / 142) | |
| dojo.js | 34.88% | (270 / 774) | 31.42% | (203 / 646) | 37.61% | (41 / 109) | 35.03% | (269 / 768) | |
| dojo.profile.js | 37.5% | (3 / 8) | 0% | (0 / 13) | 20% | (1 / 5) | 37.5% | (3 / 8) | |
| dom-attr.js | 5% | (2 / 40) | 0% | (0 / 52) | 0% | (0 / 7) | 5% | (2 / 40) | |
| dom-class.js | 3.45% | (2 / 58) | 0% | (0 / 38) | 0% | (0 / 8) | 3.57% | (2 / 56) | |
| dom-construct.js | 4.81% | (5 / 104) | 0% | (0 / 73) | 0% | (0 / 12) | 4.95% | (5 / 101) | |
| dom-form.js | 4.35% | (2 / 46) | 0% | (0 / 45) | 0% | (0 / 6) | 4.35% | (2 / 46) | |
| dom-geometry.js | 2.65% | (4 / 151) | 0% | (0 / 171) | 0% | (0 / 19) | 2.65% | (4 / 151) | |
| dom-prop.js | 2.38% | (1 / 42) | 0% | (0 / 28) | 0% | (0 / 3) | 2.38% | (1 / 42) | |
| dom-style.js | 1.96% | (2 / 102) | 0% | (0 / 94) | 0% | (0 / 14) | 2.08% | (2 / 96) | |
| dom.js | 1.92% | (1 / 52) | 0% | (0 / 45) | 0% | (0 / 7) | 1.96% | (1 / 51) | |
| domReady.js | 4.55% | (3 / 66) | 0% | (0 / 33) | 0% | (0 / 13) | 5.17% | (3 / 58) | |
| fx.js | 0.54% | (1 / 185) | 0% | (0 / 68) | 0% | (0 / 47) | 0.56% | (1 / 178) | |
| gears.js | 3.85% | (1 / 26) | 0% | (0 / 12) | 0% | (0 / 2) | 4.17% | (1 / 24) | |
| has.js | 2.27% | (1 / 44) | 0% | (0 / 52) | 0% | (0 / 8) | 2.27% | (1 / 44) | |
| hash.js | 8.79% | (8 / 91) | 0% | (0 / 57) | 0% | (0 / 12) | 8.79% | (8 / 91) | |
| hccss.js | 7.14% | (1 / 14) | 0% | (0 / 10) | 0% | (0 / 3) | 7.14% | (1 / 14) | |
| html.js | 1.03% | (1 / 97) | 0% | (0 / 62) | 0% | (0 / 20) | 1.05% | (1 / 95) | |
| i18n.js | 4.81% | (9 / 187) | 0% | (0 / 113) | 0% | (0 / 38) | 5% | (9 / 180) | |
| io-query.js | 3.45% | (1 / 29) | 0% | (0 / 12) | 0% | (0 / 3) | 3.45% | (1 / 29) | |
| json.js | 3.03% | (2 / 66) | 0% | (0 / 58) | 0% | (0 / 7) | 3.08% | (2 / 65) | |
| keys.js | 50% | (1 / 2) | 0% | (0 / 8) | 0% | (0 / 1) | 50% | (1 / 2) | |
| loadInit.js | 33.33% | (1 / 3) | 100% | (0 / 0) | 0% | (0 / 2) | 33.33% | (1 / 3) | |
| main.js | 7.14% | (1 / 14) | 0% | (0 / 10) | 0% | (0 / 3) | 8.33% | (1 / 12) | |
| mouse.js | 5.13% | (2 / 39) | 0% | (0 / 24) | 0% | (0 / 16) | 5.13% | (2 / 39) | |
| node.js | 4.55% | (1 / 22) | 0% | (0 / 8) | 0% | (0 / 4) | 4.55% | (1 / 22) | |
| number.js | 0.47% | (1 / 212) | 0% | (0 / 214) | 0% | (0 / 19) | 0.52% | (1 / 192) | |
| on.js | 2.1% | (5 / 238) | 0% | (0 / 162) | 0% | (0 / 39) | 2.11% | (5 / 237) | |
| parser.js | 2.69% | (8 / 297) | 0% | (0 / 269) | 0% | (0 / 34) | 2.69% | (8 / 297) | |
| query.js | 1.57% | (2 / 127) | 0% | (0 / 71) | 0% | (0 / 44) | 1.63% | (2 / 123) | |
| ready.js | 2.13% | (1 / 47) | 0% | (0 / 36) | 0% | (0 / 6) | 2.13% | (1 / 47) | |
| regexp.js | 5.56% | (1 / 18) | 0% | (0 / 8) | 0% | (0 / 5) | 5.56% | (1 / 18) | |
| request.js | 50% | (1 / 2) | 100% | (0 / 0) | 0% | (0 / 1) | 50% | (1 / 2) | |
| require.js | 33.33% | (1 / 3) | 100% | (0 / 0) | 0% | (0 / 2) | 33.33% | (1 / 3) | |
| robot.js | 1.96% | (1 / 51) | 0% | (0 / 16) | 0% | (0 / 11) | 1.96% | (1 / 51) | |
| robotx.js | 6.52% | (3 / 46) | 0% | (0 / 18) | 0% | (0 / 11) | 6.52% | (3 / 46) | |
| router.js | 50% | (1 / 2) | 100% | (0 / 0) | 0% | (0 / 1) | 50% | (1 / 2) | |
| sniff.js | 2.78% | (1 / 36) | 0% | (0 / 59) | 0% | (0 / 1) | 2.78% | (1 / 36) | |
| string.js | 2.78% | (1 / 36) | 0% | (0 / 22) | 0% | (0 / 7) | 2.94% | (1 / 34) | |
| text.js | 1.75% | (1 / 57) | 0% | (0 / 48) | 0% | (0 / 9) | 1.75% | (1 / 57) | |
| topic.js | 20% | (1 / 5) | 100% | (0 / 0) | 0% | (0 / 3) | 20% | (1 / 5) | |
| touch.js | 5.94% | (6 / 101) | 0% | (0 / 96) | 0% | (0 / 22) | 6% | (6 / 100) | |
| uacss.js | 5.26% | (1 / 19) | 0% | (0 / 8) | 0% | (0 / 2) | 5.26% | (1 / 19) | |
| when.js | 6.67% | (1 / 15) | 0% | (0 / 17) | 0% | (0 / 2) | 6.67% | (1 / 15) | |
| window.js | 1.02% | (1 / 98) | 0% | (0 / 124) | 0% | (0 / 7) | 1.18% | (1 / 85) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 2 | define(["./_base/kernel", "./_base/lang"], function(dojo, lang){ // module: // dojo/AdapterRegistry var AdapterRegistry = dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){ // summary: // A registry to make contextual calling/searching easier. // description: // Objects of this class keep list of arrays in the form [name, check, // wrap, directReturn] that are used to determine what the contextual // result of a set of checked arguments is. All check/wrap functions // in this registry should be of the same arity. // example: // | // create a new registry // | require(["dojo/AdapterRegistry"], // | function(AdapterRegistry){ // | var reg = new AdapterRegistry(); // | reg.register("handleString", // | function(str){ // | return typeof val == "string" // | }, // | function(str){ // | // do something with the string here // | } // | ); // | reg.register("handleArr", // | dojo.isArray, // | function(arr){ // | // do something with the array here // | } // | ); // | // | // now we can pass reg.match() *either* an array or a string and // | // the value we pass will get handled by the right function // | reg.match("someValue"); // will call the first function // | reg.match(["someValue"]); // will call the second // | }); this.pairs = []; this.returnWrappers = returnWrappers || false; // Boolean }; lang.extend(AdapterRegistry, { register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){ // summary: // register a check function to determine if the wrap function or // object gets selected // name: // a way to identify this matcher. // check: // a function that arguments are passed to from the adapter's // match() function. The check function should return true if the // given arguments are appropriate for the wrap function. // directReturn: // If directReturn is true, the value passed in for wrap will be // returned instead of being called. Alternately, the // AdapterRegistry can be set globally to "return not call" using // the returnWrappers property. Either way, this behavior allows // the registry to act as a "search" function instead of a // function interception library. // override: // If override is given and true, the check function will be given // highest priority. Otherwise, it will be the lowest priority // adapter. this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]); }, match: function(/* ... */){ // summary: // Find an adapter for the given arguments. If no suitable adapter // is found, throws an exception. match() accepts any number of // arguments, all of which are passed to all matching functions // from the registered pairs. for(var i = 0; i < this.pairs.length; i++){ var pair = this.pairs[i]; if(pair[1].apply(this, arguments)){ if((pair[3])||(this.returnWrappers)){ return pair[2]; }else{ return pair[2].apply(this, arguments); } } } throw new Error("No match found"); }, unregister: function(name){ // summary: // Remove a named adapter from the registry // name: String // The name of the adapter. // returns: Boolean // Returns true if operation is successful. // Returns false if operation fails. // FIXME: this is kind of a dumb way to handle this. On a large // registry this will be slow-ish and we can use the name as a lookup // should we choose to trade memory for speed. for(var i = 0; i < this.pairs.length; i++){ var pair = this.pairs[i]; if(pair[0] == name){ this.pairs.splice(i, 1); return true; } } return false; } }); return AdapterRegistry; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 | 1 | define([ "./has", "./_base/lang", "./errors/CancelError", "./promise/Promise", "./has!config-deferredInstrumentation?./promise/instrumentation" ], function(has, lang, CancelError, Promise, instrumentation){ "use strict"; // module: // dojo/Deferred var PROGRESS = 0, RESOLVED = 1, REJECTED = 2; var FULFILLED_ERROR_MESSAGE = "This deferred has already been fulfilled."; var freezeObject = Object.freeze || function(){}; var signalWaiting = function(waiting, type, result, rejection, deferred){ if(has("config-deferredInstrumentation")){ if(type === REJECTED && Deferred.instrumentRejected && waiting.length === 0){ Deferred.instrumentRejected(result, false, rejection, deferred); } } for(var i = 0; i < waiting.length; i++){ signalListener(waiting[i], type, result, rejection); } }; var signalListener = function(listener, type, result, rejection){ var func = listener[type]; var deferred = listener.deferred; if(func){ try{ var newResult = func(result); if(type === PROGRESS){ if(typeof newResult !== "undefined"){ signalDeferred(deferred, type, newResult); } }else{ if(newResult && typeof newResult.then === "function"){ listener.cancel = newResult.cancel; newResult.then( // Only make resolvers if they're actually going to be used makeDeferredSignaler(deferred, RESOLVED), makeDeferredSignaler(deferred, REJECTED), makeDeferredSignaler(deferred, PROGRESS)); return; } signalDeferred(deferred, RESOLVED, newResult); } }catch(error){ signalDeferred(deferred, REJECTED, error); } }else{ signalDeferred(deferred, type, result); } if(has("config-deferredInstrumentation")){ if(type === REJECTED && Deferred.instrumentRejected){ Deferred.instrumentRejected(result, !!func, rejection, deferred.promise); } } }; var makeDeferredSignaler = function(deferred, type){ return function(value){ signalDeferred(deferred, type, value); }; }; var signalDeferred = function(deferred, type, result){ if(!deferred.isCanceled()){ switch(type){ case PROGRESS: deferred.progress(result); break; case RESOLVED: deferred.resolve(result); break; case REJECTED: deferred.reject(result); break; } } }; var Deferred = function(canceler){ // summary: // Creates a new deferred. This API is preferred over // `dojo/_base/Deferred`. // description: // Creates a new deferred, as an abstraction over (primarily) // asynchronous operations. The deferred is the private interface // that should not be returned to calling code. That's what the // `promise` is for. See `dojo/promise/Promise`. // canceler: Function? // Will be invoked if the deferred is canceled. The canceler // receives the reason the deferred was canceled as its argument. // The deferred is rejected with its return value, or a new // `dojo/errors/CancelError` instance. // promise: dojo/promise/Promise // The public promise object that clients can add callbacks to. var promise = this.promise = new Promise(); var deferred = this; var fulfilled, result, rejection; var canceled = false; var waiting = []; if(has("config-deferredInstrumentation") && Error.captureStackTrace){ Error.captureStackTrace(deferred, Deferred); Error.captureStackTrace(promise, Deferred); } this.isResolved = promise.isResolved = function(){ // summary: // Checks whether the deferred has been resolved. // returns: Boolean return fulfilled === RESOLVED; }; this.isRejected = promise.isRejected = function(){ // summary: // Checks whether the deferred has been rejected. // returns: Boolean return fulfilled === REJECTED; }; this.isFulfilled = promise.isFulfilled = function(){ // summary: // Checks whether the deferred has been resolved or rejected. // returns: Boolean return !!fulfilled; }; this.isCanceled = promise.isCanceled = function(){ // summary: // Checks whether the deferred has been canceled. // returns: Boolean return canceled; }; this.progress = function(update, strict){ // summary: // Emit a progress update on the deferred. // description: // Emit a progress update on the deferred. Progress updates // can be used to communicate updates about the asynchronous // operation before it has finished. // update: any // The progress update. Passed to progbacks. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently no progress can be emitted. // returns: dojo/promise/Promise // Returns the original promise for the deferred. if(!fulfilled){ signalWaiting(waiting, PROGRESS, update, null, deferred); return promise; }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); }else{ return promise; } }; this.resolve = function(value, strict){ // summary: // Resolve the deferred. // description: // Resolve the deferred, putting it in a success state. // value: any // The result of the deferred. Passed to callbacks. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently cannot be resolved. // returns: dojo/promise/Promise // Returns the original promise for the deferred. if(!fulfilled){ // Set fulfilled, store value. After signaling waiting listeners unset // waiting. signalWaiting(waiting, fulfilled = RESOLVED, result = value, null, deferred); waiting = null; return promise; }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); }else{ return promise; } }; var reject = this.reject = function(error, strict){ // summary: // Reject the deferred. // description: // Reject the deferred, putting it in an error state. // error: any // The error result of the deferred. Passed to errbacks. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently cannot be rejected. // returns: dojo/promise/Promise // Returns the original promise for the deferred. if(!fulfilled){ if(has("config-deferredInstrumentation") && Error.captureStackTrace){ Error.captureStackTrace(rejection = {}, reject); } signalWaiting(waiting, fulfilled = REJECTED, result = error, rejection, deferred); waiting = null; return promise; }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); }else{ return promise; } }; this.then = promise.then = function(callback, errback, progback){ // summary: // Add new callbacks to the deferred. // description: // Add new callbacks to the deferred. Callbacks can be added // before or after the deferred is fulfilled. // callback: Function? // Callback to be invoked when the promise is resolved. // Receives the resolution value. // errback: Function? // Callback to be invoked when the promise is rejected. // Receives the rejection error. // progback: Function? // Callback to be invoked when the promise emits a progress // update. Receives the progress update. // returns: dojo/promise/Promise // Returns a new promise for the result of the callback(s). // This can be used for chaining many asynchronous operations. var listener = [progback, callback, errback]; // Ensure we cancel the promise we're waiting for, or if callback/errback // have returned a promise, cancel that one. listener.cancel = promise.cancel; listener.deferred = new Deferred(function(reason){ // Check whether cancel is really available, returned promises are not // required to expose `cancel` return listener.cancel && listener.cancel(reason); }); if(fulfilled && !waiting){ signalListener(listener, fulfilled, result, rejection); }else{ waiting.push(listener); } return listener.deferred.promise; }; this.cancel = promise.cancel = function(reason, strict){ // summary: // Inform the deferred it may cancel its asynchronous operation. // description: // Inform the deferred it may cancel its asynchronous operation. // The deferred's (optional) canceler is invoked and the // deferred will be left in a rejected state. Can affect other // promises that originate with the same deferred. // reason: any // A message that may be sent to the deferred's canceler, // explaining why it's being canceled. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently cannot be canceled. // returns: any // Returns the rejection reason if the deferred was canceled // normally. if(!fulfilled){ // Cancel can be called even after the deferred is fulfilled if(canceler){ var returnedReason = canceler(reason); reason = typeof returnedReason === "undefined" ? reason : returnedReason; } canceled = true; if(!fulfilled){ // Allow canceler to provide its own reason, but fall back to a CancelError if(typeof reason === "undefined"){ reason = new CancelError(); } reject(reason); return reason; }else if(fulfilled === REJECTED && result === reason){ return reason; } }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); } }; freezeObject(promise); }; Deferred.prototype.toString = function(){ // returns: String // Returns `[object Deferred]`. return "[object Deferred]"; }; if(instrumentation){ instrumentation(Deferred); } return Deferred; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | 2 1 | define(["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){ // module: // dojo/DeferredList dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){ // summary: // Deprecated, use dojo/promise/all instead. // Provides event handling for a group of Deferred objects. // description: // DeferredList takes an array of existing deferreds and returns a new deferred of its own // this new deferred will typically have its callback fired when all of the deferreds in // the given list have fired their own deferreds. The parameters `fireOnOneCallback` and // fireOnOneErrback, will fire before all the deferreds as appropriate // list: // The list of deferreds to be synchronizied with this DeferredList // fireOnOneCallback: // Will cause the DeferredLists callback to be fired as soon as any // of the deferreds in its list have been fired instead of waiting until // the entire list has finished // fireonOneErrback: // Will cause the errback to fire upon any of the deferreds errback // canceller: // A deferred canceller function, see dojo.Deferred var resultList = []; Deferred.call(this); var self = this; if(list.length === 0 && !fireOnOneCallback){ this.resolve([0, []]); } var finished = 0; darray.forEach(list, function(item, i){ item.then(function(result){ if(fireOnOneCallback){ self.resolve([i, result]); }else{ addResult(true, result); } },function(error){ if(fireOnOneErrback){ self.reject(error); }else{ addResult(false, error); } if(consumeErrors){ return null; } throw error; }); function addResult(succeeded, result){ resultList[i] = [succeeded, result]; finished++; if(finished === list.length){ self.resolve(resultList); } } }); }; dojo.DeferredList.prototype = new Deferred(); dojo.DeferredList.prototype.gatherResults = function(deferredList){ // summary: // Gathers the results of the deferreds for packaging // as the parameters to the Deferred Lists' callback // deferredList: dojo/DeferredList // The deferred list from which this function gathers results. // returns: dojo/DeferredList // The newly created deferred list which packs results as // parameters to its callback. var d = new dojo.DeferredList(deferredList, false, true, false); d.addCallback(function(results){ var ret = []; darray.forEach(results, function(result){ ret.push(result[1]); }); return ret; }); return d; }; return dojo.DeferredList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 1 1 | define(["./aspect", "./on"], function(aspect, on){ // module: // dojo/Evented "use strict"; var after = aspect.after; function Evented(){ // summary: // A class that can be used as a mixin or base class, // to add on() and emit() methods to a class // for listening for events and emitting events: // example: // | define(["dojo/Evented", "dojo/_base/declare", "dojo/Stateful" // | ], function(Evented, declare, Stateful){ // | var EventedStateful = declare([Evented, Stateful], {...}); // | var instance = new EventedStateful(); // | instance.on("open", function(event){ // | ... do something with event // | }); // | // | instance.emit("open", {name:"some event", ...}); } Evented.prototype = { on: function(type, listener){ return on.parse(this, type, listener, function(target, type){ return after(target, 'on' + type, listener, true); }); }, emit: function(type, event){ var args = [this]; args.push.apply(args, arguments); return on.emit.apply(on, args); } }; return Evented; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | 1 | define([ "./_base/kernel", "./query", "./_base/lang", "./_base/array", "./dom-attr" ], function(dojo, query, lang, array, attr){ // module: // dojo/NodeList-data /*===== return function(){ // summary: // Adds data() and removeData() methods to NodeList, and returns NodeList constructor. }; =====*/ var NodeList = query.NodeList; var dataCache = {}, x = 0, dataattr = "data-dojo-dataid", dopid = function(node){ // summary: // Return a uniqueish ID for the passed node reference var pid = attr.get(node, dataattr); if(!pid){ pid = "pid" + (x++); attr.set(node, dataattr, pid); } return pid; } ; //>>excludeStart("debugging", true); // An alias to the private dataCache for NodeList-data. NEVER USE THIS! // This private is only exposed for the benefit of unit testing, and is // removed during the build process. NodeList._nodeDataCache = dojo._nodeDataCache = dataCache; //>>excludeEnd("debugging"); var dodata = dojo._nodeData = function(node, key, value){ // summary: // Private helper for dojo/NodeList.data for single node data access. Refer to NodeList.data // documentation for more information. // // node: String|DomNode // The node to associate data with // // key: Object|String? // If an object, act as a setter and iterate over said object setting data items as defined. // If a string, and `value` present, set the data for defined `key` to `value` // If a string, and `value` absent, act as a getter, returning the data associated with said `key` // // value: Anything? // The value to set for said `key`, provided `key` is a string (and not an object) // var pid = dopid(node), r; if(!dataCache[pid]){ dataCache[pid] = {}; } // API discrepency: calling with only a node returns the whole object. $.data throws if(arguments.length == 1){ r = dataCache[pid]; } if(typeof key == "string"){ // either getter or setter, based on `value` presence if(arguments.length > 2){ dataCache[pid][key] = value; }else{ r = dataCache[pid][key]; } }else{ // must be a setter, mix `value` into data hash // API discrepency: using object as setter works here r = lang.mixin(dataCache[pid], key); } return r; // Object|Anything|Nothing }; var removeData = dojo._removeNodeData = function(node, key){ // summary: // Remove some data from this node // node: String|DomNode // The node reference to remove data from // key: String? // If omitted, remove all data in this dataset. // If passed, remove only the passed `key` in the associated dataset var pid = dopid(node); if(dataCache[pid]){ if(key){ delete dataCache[pid][key]; }else{ delete dataCache[pid]; } } }; NodeList._gcNodeData = dojo._gcNodeData = function(){ // summary: // super expensive: GC all data in the data for nodes that no longer exist in the dom. // description: // super expensive: GC all data in the data for nodes that no longer exist in the dom. // MUCH safer to do this yourself, manually, on a per-node basis (via `NodeList.removeData()`) // provided as a stop-gap for exceptionally large/complex applications with constantly changing // content regions (eg: a dijit/layout/ContentPane with replacing data) // There is NO automatic GC going on. If you dojo.destroy() a node, you should _removeNodeData // prior to destruction. var livePids = query("[" + dataattr + "]").map(dopid); for(var i in dataCache){ if(array.indexOf(livePids, i) < 0){ delete dataCache[i]; } } }; // make nodeData and removeNodeData public on dojo/NodeList: lang.extend(NodeList, { data: NodeList._adaptWithCondition(dodata, function(a){ return a.length === 0 || a.length == 1 && (typeof a[0] == "string"); }), removeData: NodeList._adaptAsForEach(removeData) }); /*===== lang.extend(NodeList, { data: function(key, value){ // summary: // stash or get some arbitrary data on/from these nodes. // // description: // Stash or get some arbitrary data on/from these nodes. This private _data function is // exposed publicly on `dojo/NodeList`, eg: as the result of a `dojo/query` call. // DIFFERS from jQuery.data in that when used as a getter, the entire list is ALWAYS // returned. EVEN WHEN THE LIST IS length == 1. // // A single-node version of this function is provided as `dojo._nodeData`, which follows // the same signature, though expects a String ID or DomNode reference in the first // position, before key/value arguments. // // node: String|DomNode // The node to associate data with // // key: Object|String? // If an object, act as a setter and iterate over said object setting data items as defined. // If a string, and `value` present, set the data for defined `key` to `value` // If a string, and `value` absent, act as a getter, returning the data associated with said `key` // // value: Anything? // The value to set for said `key`, provided `key` is a string (and not an object) // // example: // Set a key `bar` to some data, then retrieve it. // | require(["dojo/query", "dojo/NodeList-data"], function(query){ // | query(".foo").data("bar", "touched"); // | var touched = query(".foo").data("bar"); // | if(touched[0] == "touched"){ alert('win'); } // | }); // // example: // Get all the data items for a given node. // | require(["dojo/query", "dojo/NodeList-data"], function(query){ // | var list = query(".foo").data(); // | var first = list[0]; // | }); // // example: // Set the data to a complex hash. Overwrites existing keys with new value // | require(["dojo/query", "dojo/NodeList-data"], function(query){ // | query(".foo").data({ bar:"baz", foo:"bar" }); // Then get some random key: // | query(".foo").data("foo"); // returns [`bar`] // | }); // // returns: Object|Anything|Nothing // When used as a setter via `dojo/NodeList`, a NodeList instance is returned // for further chaining. When used as a getter via `dojo/NodeList` an ARRAY // of items is returned. The items in the array correspond to the elements // in the original list. This is true even when the list length is 1, eg: // when looking up a node by ID (#foo) }, removeData: function(key){ // summary: // Remove the data associated with these nodes. // key: String? // If omitted, clean all data for this node. // If passed, remove the data item found at `key` } }); =====*/ // TODO: this is the basic implementation of adaptWithConditionAndWhenMappedConsiderLength, for lack of a better API name // it conflicts with the the `dojo/NodeList` way: always always return an arrayLike thinger. Consider for 2.0: // // NodeList.prototype.data = function(key, value){ // var a = arguments, r; // if(a.length === 0 || a.length == 1 && (typeof a[0] == "string")){ // r = this.map(function(node){ // return d._data(node, key); // }); // if(r.length == 1){ r = r[0]; } // the offending line, and the diff on adaptWithCondition // }else{ // r = this.forEach(function(node){ // d._data(node, key, value); // }); // } // return r; // NodeList|Array|SingleItem // }; return NodeList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 | 1 1 | define(["./_base/kernel", "./query", "./_base/array", "./_base/lang", "./dom-class", "./dom-construct", "./dom-geometry", "./dom-attr", "./dom-style"], function(dojo, query, array, lang, domCls, domCtr, domGeom, domAttr, domStyle){ // module: // dojo/NodeList-dom.js /*===== return function(){ // summary: // Adds DOM related methods to NodeList, and returns NodeList constructor. }; =====*/ var magicGuard = function(a){ // summary: // the guard function for dojo/dom-attr() and dojo/dom-style() return a.length == 1 && (typeof a[0] == "string"); // inline'd type check }; var orphan = function(node){ // summary: // function to orphan nodes var p = node.parentNode; if(p){ p.removeChild(node); } }; // FIXME: should we move orphan() to dojo/_base/html? var NodeList = query.NodeList, awc = NodeList._adaptWithCondition, aafe = NodeList._adaptAsForEach, aam = NodeList._adaptAsMap; function getSet(module){ return function(node, name, value){ if(arguments.length == 2){ return module[typeof name == "string" ? "get" : "set"](node, name); } // setter return module.set(node, name, value); }; } lang.extend(NodeList, { _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){ // summary: // normalizes data to an array of items to insert. // description: // If content is an object, it can have special properties "template" and // "parse". If "template" is defined, then the template value is run through // dojo/string.substitute (if dojo/string.substitute() has been required elsewhere), // or if templateFunc is a function on the content, that function will be used to // transform the template into a final string to be used for for passing to dojo/dom-construct.toDom(). // If content.parse is true, then it is remembered for later, for when the content // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets // (if dojo/parser has been required elsewhere). //Wanted to just use a DocumentFragment, but for the array/NodeList //case that meant using cloneNode, but we may not want that. //Cloning should only happen if the node operations span //multiple refNodes. Also, need a real array, not a NodeList from the //DOM since the node movements could change those NodeLists. var parse = content.parse === true; //Do we have an object that needs to be run through a template? if(typeof content.template == "string"){ var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute); content = templateFunc ? templateFunc(content.template, content) : content; } var type = (typeof content); if(type == "string" || type == "number"){ content = domCtr.toDom(content, (refNode && refNode.ownerDocument)); if(content.nodeType == 11){ //DocumentFragment. It cannot handle cloneNode calls, so pull out the children. content = lang._toArray(content.childNodes); }else{ content = [content]; } }else if(!lang.isArrayLike(content)){ content = [content]; }else if(!lang.isArray(content)){ //To get to this point, content is array-like, but //not an array, which likely means a DOM NodeList. Convert it now. content = lang._toArray(content); } //Pass around the parse info if(parse){ content._runParse = true; } return content; //Array }, _cloneNode: function(/*DOMNode*/ node){ // summary: // private utility to clone a node. Not very interesting in the vanilla // dojo/NodeList case, but delegates could do interesting things like // clone event handlers if that is derivable from the node. return node.cloneNode(true); }, _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){ // summary: // private utility to handle placing an array of nodes relative to another node. // description: // Allows for cloning the nodes in the array, and for // optionally parsing widgets, if ary._runParse is true. //Avoid a disallowed operation if trying to do an innerHTML on a non-element node. if(refNode.nodeType != 1 && position == "only"){ return; } var rNode = refNode, tempNode; //Always cycle backwards in case the array is really a //DOM NodeList and the DOM operations take it out of the live collection. var length = ary.length; for(var i = length - 1; i >= 0; i--){ var node = (useClone ? this._cloneNode(ary[i]) : ary[i]); //If need widget parsing, use a temp node, instead of waiting after inserting into //real DOM because we need to start widget parsing at one node up from current node, //which could cause some already parsed widgets to be parsed again. if(ary._runParse && dojo.parser && dojo.parser.parse){ if(!tempNode){ tempNode = rNode.ownerDocument.createElement("div"); } tempNode.appendChild(node); dojo.parser.parse(tempNode); node = tempNode.firstChild; while(tempNode.firstChild){ tempNode.removeChild(tempNode.firstChild); } } if(i == length - 1){ domCtr.place(node, rNode, position); }else{ rNode.parentNode.insertBefore(node, rNode); } rNode = node; } }, position: aam(domGeom.position), /*===== position: function(){ // summary: // Returns border-box objects (x/y/w/h) of all elements in a node list // as an Array (*not* a NodeList). Acts like `dojo/dom-geometry-position`, though // assumes the node passed is each node in this list. return dojo.map(this, dojo.position); // Array }, =====*/ attr: awc(getSet(domAttr), magicGuard), /*===== attr: function(property, value){ // summary: // gets or sets the DOM attribute for every element in the // NodeList. See also `dojo/dom-attr` // property: String // the attribute to get/set // value: String? // optional. The value to set the property to // returns: // if no value is passed, the result is an array of attribute values // If a value is passed, the return is this NodeList // example: // Make all nodes with a particular class focusable: // | require(["dojo/query", "dojo/NodeList-dom"], function(query){ // | query(".focusable").attr("tabIndex", -1); // | }); // example: // Disable a group of buttons: // | require(["dojo/query", "dojo/NodeList-dom"], function(query){ // | query("button.group").attr("disabled", true); // | }); // example: // innerHTML can be assigned or retrieved as well: // | // get the innerHTML (as an array) for each list item // | require(["dojo/query", "dojo/NodeList-dom"], function(query){ // | var ih = query("li.replaceable").attr("innerHTML"); // | }); return; // dojo/NodeList|Array }, =====*/ style: awc(getSet(domStyle), magicGuard), /*===== style: function(property, value){ // summary: // gets or sets the CSS property for every element in the NodeList // property: String // the CSS property to get/set, in JavaScript notation // ("lineHieght" instead of "line-height") // value: String? // optional. The value to set the property to // returns: // if no value is passed, the result is an array of strings. // If a value is passed, the return is this NodeList return; // dojo/NodeList return; // Array }, =====*/ addClass: aafe(domCls.add), /*===== addClass: function(className){ // summary: // adds the specified class to every node in the list // className: String|Array // A String class name to add, or several space-separated class names, // or an array of class names. return; // dojo/NodeList }, =====*/ removeClass: aafe(domCls.remove), /*===== removeClass: function(className){ // summary: // removes the specified class from every node in the list // className: String|Array? // An optional String class name to remove, or several space-separated // class names, or an array of class names. If omitted, all class names // will be deleted. // returns: // this list return; // dojo/NodeList }, =====*/ toggleClass: aafe(domCls.toggle), /*===== toggleClass: function(className, condition){ // summary: // Adds a class to node if not present, or removes if present. // Pass a boolean condition if you want to explicitly add or remove. // condition: Boolean? // If passed, true means to add the class, false means to remove. // className: String // the CSS class to add return; // dojo/NodeList }, =====*/ replaceClass: aafe(domCls.replace), /*===== replaceClass: function(addClassStr, removeClassStr){ // summary: // Replaces one or more classes on a node if not present. // Operates more quickly than calling `removeClass()` and `addClass()` // addClassStr: String|Array // A String class name to add, or several space-separated class names, // or an array of class names. // removeClassStr: String|Array? // A String class name to remove, or several space-separated class names, // or an array of class names. return; // dojo/NodeList }, =====*/ empty: aafe(domCtr.empty), /*===== empty: function(){ // summary: // clears all content from each node in the list. Effectively // equivalent to removing all child nodes from every item in // the list. return this.forEach("item.innerHTML='';"); // dojo/NodeList // FIXME: should we be checking for and/or disposing of widgets below these nodes? }, =====*/ removeAttr: aafe(domAttr.remove), /*===== removeAttr: function(name){ // summary: // Removes an attribute from each node in the list. // name: String // the name of the attribute to remove return; // dojo/NodeList }, =====*/ marginBox: aam(domGeom.getMarginBox), /*===== marginBox: function(){ // summary: // Returns margin-box size of nodes return; // dojo/NodeList }, =====*/ // FIXME: connectPublisher()? connectRunOnce()? /* destroy: function(){ // summary: // destroys every item in the list. this.forEach(d.destroy); // FIXME: should we be checking for and/or disposing of widgets below these nodes? }, */ place: function(/*String||Node*/ queryOrNode, /*String*/ position){ // summary: // places elements of this node list relative to the first element matched // by queryOrNode. Returns the original NodeList. See: `dojo/dom-construct.place` // queryOrNode: // may be a string representing any valid CSS3 selector or a DOM node. // In the selector case, only the first matching element will be used // for relative positioning. // position: // can be one of: // // - "last" (default) // - "first" // - "before" // - "after" // - "only" // - "replace" // // or an offset in the childNodes property var item = query(queryOrNode)[0]; return this.forEach(function(node){ domCtr.place(node, item, position); }); // dojo/NodeList }, orphan: function(/*String?*/ filter){ // summary: // removes elements in this list that match the filter // from their parents and returns them as a new NodeList. // filter: // CSS selector like ".foo" or "div > span" // returns: // NodeList containing the orphaned elements return (filter ? query._filterResult(this, filter) : this).forEach(orphan); // dojo/NodeList }, adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){ // summary: // places any/all elements in queryOrListOrNode at a // position relative to the first element in this list. // Returns a dojo/NodeList of the adopted elements. // queryOrListOrNode: // a DOM node or a query string or a query result. // Represents the nodes to be adopted relative to the // first element of this NodeList. // position: // can be one of: // // - "last" (default) // - "first" // - "before" // - "after" // - "only" // - "replace" // // or an offset in the childNodes property return query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo/NodeList }, // FIXME: do we need this? query: function(/*String*/ queryStr){ // summary: // Returns a new list whose members match the passed query, // assuming elements of the current NodeList as the root for // each search. // example: // assume a DOM created by this markup: // | <div id="foo"> // | <p> // | bacon is tasty, <span>dontcha think?</span> // | </p> // | </div> // | <div id="bar"> // | <p>great comedians may not be funny <span>in person</span></p> // | </div> // If we are presented with the following definition for a NodeList: // | require(["dojo/dom", "dojo/query", "dojo/NodeList-dom" // | ], function(dom, query){ // | var l = new NodeList(dom.byId("foo"), dom.byId("bar")); // it's possible to find all span elements under paragraphs // contained by these elements with this sub-query: // | var spans = l.query("p span"); // | }); // FIXME: probably slow if(!queryStr){ return this; } var ret = new NodeList; this.map(function(node){ // FIXME: why would we ever get undefined here? query(queryStr, node).forEach(function(subNode){ if(subNode !== undefined){ ret.push(subNode); } }); }); return ret._stash(this); // dojo/NodeList }, filter: function(/*String|Function*/ filter){ // summary: // "masks" the built-in javascript filter() method (supported // in Dojo via `dojo.filter`) to support passing a simple // string filter in addition to supporting filtering function // objects. // filter: // If a string, a CSS rule like ".thinger" or "div > span". // example: // "regular" JS filter syntax as exposed in dojo.filter: // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("*").filter(function(item){ // | // highlight every paragraph // | return (item.nodeName == "p"); // | }).style("backgroundColor", "yellow"); // | }); // example: // the same filtering using a CSS selector // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("*").filter("p").styles("backgroundColor", "yellow"); // | }); var a = arguments, items = this, start = 0; if(typeof filter == "string"){ // inline'd type check items = query._filterResult(this, a[0]); if(a.length == 1){ // if we only got a string query, pass back the filtered results return items._stash(this); // dojo/NodeList } // if we got a callback, run it over the filtered items start = 1; } return this._wrap(array.filter(items, a[start], a[start + 1]), this); // dojo/NodeList }, /* // FIXME: should this be "copyTo" and include parenting info? clone: function(){ // summary: // creates node clones of each element of this list // and returns a new list containing the clones }, */ addContent: function(/*String||DomNode||Object||dojo/NodeList*/ content, /*String||Integer?*/ position){ // summary: // add a node, NodeList or some HTML as a string to every item in the // list. Returns the original list. // description: // a copy of the HTML content is added to each item in the // list, with an optional position argument. If no position // argument is provided, the content is appended to the end of // each item. // content: // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or // NodeList, the content will be cloned if the current NodeList has more than one // element. Only the DOM nodes are cloned, no event handlers. If it is an Object, // it should be an object with at "template" String property that has the HTML string // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute // will be used on the "template" to generate the final HTML string. Other allowed // properties on the object are: "parse" if the HTML // string should be parsed for widgets (dojo.require("dojo.parser") to get that // option to work), and "templateFunc" if a template function besides dojo.string.substitute // should be used to transform the "template". // position: // can be one of: // // - "last"||"end" (default) // - "first||"start" // - "before" // - "after" // - "replace" (replaces nodes in this NodeList with new content) // - "only" (removes other children of the nodes so new content is the only child) // // or an offset in the childNodes property // example: // appends content to the end if the position is omitted // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("h3 > p").addContent("hey there!"); // | }); // example: // add something to the front of each element that has a // "thinger" property: // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("[thinger]").addContent("...", "first"); // | }); // example: // adds a header before each element of the list // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query(".note").addContent("<h4>NOTE:</h4>", "before"); // | }); // example: // add a clone of a DOM node to the end of every element in // the list, removing it from its existing parent. // | require(["dojo/dom", "dojo/query", "dojo/NodeList-dom" // | ], function(dom, query){ // | query(".note").addContent(dom.byId("foo")); // | }); // example: // Append nodes from a templatized string. // | require(["dojo/string", "dojo/query", "dojo/NodeList-dom" // | ], function(string, query){ // | query(".note").addContent({ // | template: '<b>${id}: </b><span>${name}</span>', // | id: "user332", // | name: "Mr. Anderson" // | }); // | }); // example: // Append nodes from a templatized string that also has widgets parsed. // | require(["dojo/string", "dojo/parser", "dojo/query", "dojo/NodeList-dom" // | ], function(string, parser, query){ // | var notes = query(".note").addContent({ // | template: '<button dojoType="dijit/form/Button">${text}</button>', // | parse: true, // | text: "Send" // | }); // | }); content = this._normalize(content, this[0]); for(var i = 0, node; (node = this[i]); i++){ if(content.length){ this._place(content, node, position, i > 0); }else{ // if it is an empty array, we empty the target node domCtr.empty(node); } } return this; // dojo/NodeList } }); return NodeList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | 1 | define(["./query", "./_base/lang", "./aspect", "./_base/fx", "./fx"], function(query, lang, aspect, baseFx, coreFx){ // module: // dojo/NodeList-fx /*===== return function(){ // summary: // Adds dojo.fx animation support to dojo.query() by extending the NodeList class // with additional FX functions. NodeList is the array-like object used to hold query results. }; =====*/ var NodeList = query.NodeList; lang.extend(NodeList, { _anim: function(obj, method, args){ args = args||{}; var a = coreFx.combine( this.map(function(item){ var tmpArgs = { node: item }; lang.mixin(tmpArgs, args); return obj[method](tmpArgs); }) ); return args.auto ? a.play() && this : a; // dojo/_base/fx.Animation|dojo/NodeList }, wipeIn: function(args){ // summary: // wipe in all elements of this NodeList via `dojo/fx.wipeIn()` // // args: Object? // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of // an `auto` parameter. // // returns: dojo/_base/fx.Animation|dojo/NodeList // A special args member `auto` can be passed to automatically play the animation. // If args.auto is present, the original dojo/NodeList will be returned for further // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed // // example: // Fade in all tables with class "blah": // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query("table.blah").wipeIn().play(); // | }); // // example: // Utilizing `auto` to get the NodeList back: // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query(".titles").wipeIn({ auto:true }).onclick(someFunction); // | }); // return this._anim(coreFx, "wipeIn", args); // dojo/_base/fx.Animation|dojo/NodeList }, wipeOut: function(args){ // summary: // wipe out all elements of this NodeList via `dojo/fx.wipeOut()` // // args: Object? // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of // an `auto` parameter. // // returns: dojo/_base/fx.Animation|dojo/NodeList // A special args member `auto` can be passed to automatically play the animation. // If args.auto is present, the original dojo/NodeList will be returned for further // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed // // example: // Wipe out all tables with class "blah": // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query("table.blah").wipeOut().play(); // | }); return this._anim(coreFx, "wipeOut", args); // dojo/_base/fx.Animation|dojo/NodeList }, slideTo: function(args){ // summary: // slide all elements of the node list to the specified place via `dojo/fx.slideTo()` // // args: Object? // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of // an `auto` parameter. // // returns: dojo/_base/fx.Animation|dojo/NodeList // A special args member `auto` can be passed to automatically play the animation. // If args.auto is present, the original dojo/NodeList will be returned for further // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed // // example: // | Move all tables with class "blah" to 300/300: // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query("table.blah").slideTo({ // | left: 40, // | top: 50 // | }).play(); // | }); return this._anim(coreFx, "slideTo", args); // dojo/_base/fx.Animation|dojo/NodeList }, fadeIn: function(args){ // summary: // fade in all elements of this NodeList via `dojo.fadeIn` // // args: Object? // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of // an `auto` parameter. // // returns: dojo/_base/fx.Animation|dojo/NodeList // A special args member `auto` can be passed to automatically play the animation. // If args.auto is present, the original dojo/NodeList will be returned for further // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed // // example: // Fade in all tables with class "blah": // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query("table.blah").fadeIn().play(); // | }); return this._anim(baseFx, "fadeIn", args); // dojo/_base/fx.Animation|dojo/NodeList }, fadeOut: function(args){ // summary: // fade out all elements of this NodeList via `dojo.fadeOut` // // args: Object? // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of // an `auto` parameter. // // returns: dojo/_base/fx.Animation|dojo/NodeList // A special args member `auto` can be passed to automatically play the animation. // If args.auto is present, the original dojo/NodeList will be returned for further // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed // // example: // Fade out all elements with class "zork": // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query(".zork").fadeOut().play(); // | }); // example: // Fade them on a delay and do something at the end: // | require(["dojo/query", "dojo/aspect", "dojo/NodeList-fx" // | ], function(query, aspect){ // | var fo = query(".zork").fadeOut(); // | aspect.after(fo, "onEnd", function(){ /*...*/ }, true); // | fo.play(); // | }); // example: // Using `auto`: // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit); // | }); // return this._anim(baseFx, "fadeOut", args); // dojo/_base/fx.Animation|dojo/NodeList }, animateProperty: function(args){ // summary: // Animate all elements of this NodeList across the properties specified. // syntax identical to `dojo.animateProperty` // // args: Object? // Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of // an `auto` parameter. // // returns: dojo/_base/fx.Animation|dojo/NodeList // A special args member `auto` can be passed to automatically play the animation. // If args.auto is present, the original dojo/NodeList will be returned for further // chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed // // example: // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query(".zork").animateProperty({ // | duration: 500, // | properties: { // | color: { start: "black", end: "white" }, // | left: { end: 300 } // | } // | }).play(); // | }); // // example: // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query(".grue").animateProperty({ // | auto:true, // | properties: { // | height:240 // | } // | }).onclick(handler); // | }); return this._anim(baseFx, "animateProperty", args); // dojo/_base/fx.Animation|dojo/NodeList }, anim: function( /*Object*/ properties, /*Integer?*/ duration, /*Function?*/ easing, /*Function?*/ onEnd, /*Integer?*/ delay){ // summary: // Animate one or more CSS properties for all nodes in this list. // The returned animation object will already be playing when it // is returned. See the docs for `dojo.anim` for full details. // properties: Object // the properties to animate. does NOT support the `auto` parameter like other // NodeList-fx methods. // duration: Integer? // Optional. The time to run the animations for // easing: Function? // Optional. The easing function to use. // onEnd: Function? // A function to be called when the animation ends // delay: // how long to delay playing the returned animation // example: // Another way to fade out: // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query(".thinger").anim({ opacity: 0 }); // | }); // example: // animate all elements with the "thigner" class to a width of 500 // pixels over half a second // | require(["dojo/query", "dojo/NodeList-fx" // | ], function(query){ // | query(".thinger").anim({ width: 500 }, 700); // | }); var canim = coreFx.combine( this.map(function(item){ return baseFx.animateProperty({ node: item, properties: properties, duration: duration||350, easing: easing }); }) ); if(onEnd){ aspect.after(canim, "onEnd", onEnd, true); } return canim.play(delay||0); // dojo/_base/fx.Animation } }); return NodeList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 1 | define(["./query", "./_base/lang", "./html"], function(query, lang, html){ // module: // dojo/NodeList-html /*===== return function(){ // summary: // Adds a chainable html method to dojo/query() / NodeList instances for setting/replacing node content }; =====*/ var NodeList = query.NodeList; lang.extend(NodeList, { html: function(/* String|DomNode|NodeList? */ content, /* Object? */params){ // summary: // see `dojo/html.set()`. Set the content of all elements of this NodeList // // content: // An html string, node or enumerable list of nodes for insertion into the dom // // params: // Optional flags/properties to configure the content-setting. See dojo/html._ContentSetter // // description: // Based around `dojo/html.set()`, set the content of the Elements in a // NodeList to the given content (string/node/nodelist), with optional arguments // to further tune the set content behavior. // // example: // | require(["dojo/query", "dojo/NodeList-html" // | ], function(query){ // | query(".thingList").html("<li data-dojo-type='dojo/dnd/Moveable'>1</li><li data-dojo-type='dojo/dnd/Moveable'>2</li><li data-dojo-type='dojo/dnd/Moveable'>3</li>", // | { // | parseContent: true, // | onBegin: function(){ // | this.content = this.content.replace(/([0-9])/g, this.id + ": $1"); // | this.inherited("onBegin", arguments); // | } // | }).removeClass("notdone").addClass("done"); // | }); var dhs = new html._ContentSetter(params || {}); this.forEach(function(elm){ dhs.node = elm; dhs.set(content); dhs.tearDown(); }); return this; // dojo/NodeList } }); return NodeList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | 1 1 1 1 | define(["./query", "./_base/lang", "./_base/array", "./dom-construct", "./NodeList-dom"], function(dquery, lang, array, construct){ // module: // dojo/NodeList-manipulate /*===== return function(){ // summary: // Adds chainable methods to dojo.query() / NodeList instances for manipulating HTML // and DOM nodes and their properties. }; =====*/ var NodeList = dquery.NodeList; //TODO: add a way to parse for widgets in the injected markup? function getText(/*DOMNode*/node){ // summary: // recursion method for text() to use. Gets text value for a node. // description: // Juse uses nodedValue so things like <br/> tags do not end up in // the text as any sort of line return. var text = "", ch = node.childNodes; for(var i = 0, n; n = ch[i]; i++){ //Skip comments. if(n.nodeType != 8){ if(n.nodeType == 1){ text += getText(n); }else{ text += n.nodeValue; } } } return text; } function getWrapInsertion(/*DOMNode*/node){ // summary: // finds the innermost element to use for wrap insertion. //Make it easy, assume single nesting, no siblings. while(node.childNodes[0] && node.childNodes[0].nodeType == 1){ node = node.childNodes[0]; } return node; //DOMNode } function makeWrapNode(/*DOMNode||String*/html, /*DOMNode*/refNode){ // summary: // convert HTML into nodes if it is not already a node. if(typeof html == "string"){ html = construct.toDom(html, (refNode && refNode.ownerDocument)); if(html.nodeType == 11){ //DocumentFragment cannot handle cloneNode, so choose first child. html = html.childNodes[0]; } }else if(html.nodeType == 1 && html.parentNode){ //This element is already in the DOM clone it, but not its children. html = html.cloneNode(false); } return html; /*DOMNode*/ } lang.extend(NodeList, { _placeMultiple: function(/*String||Node||NodeList*/query, /*String*/position){ // summary: // private method for inserting queried nodes into all nodes in this NodeList // at different positions. Differs from NodeList.place because it will clone // the nodes in this NodeList if the query matches more than one element. var nl2 = typeof query == "string" || query.nodeType ? dquery(query) : query; var toAdd = []; for(var i = 0; i < nl2.length; i++){ //Go backwards in DOM to make dom insertions easier via insertBefore var refNode = nl2[i]; var length = this.length; for(var j = length - 1, item; item = this[j]; j--){ if(i > 0){ //Need to clone the item. This also means //it needs to be added to the current NodeList //so it can also be the target of other chaining operations. item = this._cloneNode(item); toAdd.unshift(item); } if(j == length - 1){ construct.place(item, refNode, position); }else{ refNode.parentNode.insertBefore(item, refNode); } refNode = item; } } if(toAdd.length){ //Add the toAdd items to the current NodeList. Build up list of args //to pass to splice. toAdd.unshift(0); toAdd.unshift(this.length - 1); Array.prototype.splice.apply(this, toAdd); } return this; // dojo/NodeList }, innerHTML: function(/*String|DOMNode|NodeList?*/ value){ // summary: // allows setting the innerHTML of each node in the NodeList, // if there is a value passed in, otherwise, reads the innerHTML value of the first node. // description: // This method is simpler than the dojo/NodeList.html() method provided by // `dojo/NodeList-html`. This method just does proper innerHTML insertion of HTML fragments, // and it allows for the innerHTML to be read for the first node in the node list. // Since dojo/NodeList-html already took the "html" name, this method is called // "innerHTML". However, if dojo/NodeList-html has not been loaded yet, this // module will define an "html" method that can be used instead. Be careful if you // are working in an environment where it is possible that dojo/NodeList-html could // have been loaded, since its definition of "html" will take precedence. // The nodes represented by the value argument will be cloned if more than one // node is in this NodeList. The nodes in this NodeList are returned in the "set" // usage of this method, not the HTML that was inserted. // returns: // if no value is passed, the result is String, the innerHTML of the first node. // If a value is passed, the return is this dojo/NodeList // example: // assume a DOM created by this markup: // | <div id="foo"></div> // | <div id="bar"></div> // This code inserts `<p>Hello World</p>` into both divs: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("div").innerHTML("<p>Hello World</p>"); // | }); // example: // assume a DOM created by this markup: // | <div id="foo"><p>Hello Mars</p></div> // | <div id="bar"><p>Hello World</p></div> // This code returns `<p>Hello Mars</p>`: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | var message = query("div").innerHTML(); // | }); if(arguments.length){ return this.addContent(value, "only"); // dojo/NodeList }else{ return this[0].innerHTML; //String } }, /*===== html: function(value){ // summary: // see the information for "innerHTML". "html" is an alias for "innerHTML", but is // only defined if dojo/NodeList-html has not been loaded. // description: // An alias for the "innerHTML" method, but only defined if there is not an existing // "html" method on dojo/NodeList. Be careful if you are working in an environment // where it is possible that dojo/NodeList-html could have been loaded, since its // definition of "html" will take precedence. If you are not sure if dojo/NodeList-html // could be loaded, use the "innerHTML" method. // value: String|DOMNode|NodeList? // The HTML fragment to use as innerHTML. If value is not passed, then the innerHTML // of the first element in this NodeList is returned. // returns: // if no value is passed, the result is String, the innerHTML of the first node. // If a value is passed, the return is this dojo/NodeList return; // dojo/NodeList|String }, =====*/ text: function(/*String*/value){ // summary: // allows setting the text value of each node in the NodeList, // if there is a value passed in, otherwise, returns the text value for all the // nodes in the NodeList in one string. // example: // assume a DOM created by this markup: // | <div id="foo"></div> // | <div id="bar"></div> // This code inserts "Hello World" into both divs: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("div").text("Hello World"); // | }); // example: // assume a DOM created by this markup: // | <div id="foo"><p>Hello Mars <span>today</span></p></div> // | <div id="bar"><p>Hello World</p></div> // This code returns "Hello Mars today": // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | var message = query("div").text(); // | }); // returns: // if no value is passed, the result is String, the text value of the first node. // If a value is passed, the return is this dojo/NodeList if(arguments.length){ for(var i = 0, node; node = this[i]; i++){ if(node.nodeType == 1){ construct.empty(node); node.appendChild(node.ownerDocument.createTextNode(value)); } } return this; // dojo/NodeList }else{ var result = ""; for(i = 0; node = this[i]; i++){ result += getText(node); } return result; //String } }, val: function(/*String||Array*/value){ // summary: // If a value is passed, allows seting the value property of form elements in this // NodeList, or properly selecting/checking the right value for radio/checkbox/select // elements. If no value is passed, the value of the first node in this NodeList // is returned. // returns: // if no value is passed, the result is String or an Array, for the value of the // first node. // If a value is passed, the return is this dojo/NodeList // example: // assume a DOM created by this markup: // | <input type="text" value="foo"> // | <select multiple> // | <option value="red" selected>Red</option> // | <option value="blue">Blue</option> // | <option value="yellow" selected>Yellow</option> // | </select> // This code gets and sets the values for the form fields above: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query('[type="text"]').val(); //gets value foo // | query('[type="text"]').val("bar"); //sets the input's value to "bar" // | query("select").val() //gets array value ["red", "yellow"] // | query("select").val(["blue", "yellow"]) //Sets the blue and yellow options to selected. // | }); //Special work for input elements. if(arguments.length){ var isArray = lang.isArray(value); for(var index = 0, node; node = this[index]; index++){ var name = node.nodeName.toUpperCase(); var type = node.type; var newValue = isArray ? value[index] : value; if(name == "SELECT"){ var opts = node.options; for(var i = 0; i < opts.length; i++){ var opt = opts[i]; if(node.multiple){ opt.selected = (array.indexOf(value, opt.value) != -1); }else{ opt.selected = (opt.value == newValue); } } }else if(type == "checkbox" || type == "radio"){ node.checked = (node.value == newValue); }else{ node.value = newValue; } } return this; // dojo/NodeList }else{ //node already declared above. node = this[0]; if(!node || node.nodeType != 1){ return undefined; } value = node.value || ""; if(node.nodeName.toUpperCase() == "SELECT" && node.multiple){ //A multivalued selectbox. Do the pain. value = []; //opts declared above in if block. opts = node.options; //i declared above in if block; for(i = 0; i < opts.length; i++){ //opt declared above in if block opt = opts[i]; if(opt.selected){ value.push(opt.value); } } if(!value.length){ value = null; } } return value; //String||Array } }, append: function(/*String||DOMNode||NodeList*/content){ // summary: // appends the content to every node in the NodeList. // description: // The content will be cloned if the length of NodeList // is greater than 1. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the appended content. // example: // assume a DOM created by this markup: // | <div id="foo"><p>Hello Mars</p></div> // | <div id="bar"><p>Hello World</p></div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("div").append("<span>append</span>"); // | }); // Results in this DOM structure: // | <div id="foo"><p>Hello Mars</p><span>append</span></div> // | <div id="bar"><p>Hello World</p><span>append</span></div> return this.addContent(content, "last"); // dojo/NodeList }, appendTo: function(/*String*/query){ // summary: // appends nodes in this NodeList to the nodes matched by // the query passed to appendTo. // description: // The nodes in this NodeList will be cloned if the query // matches more than one element. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the matched nodes from the query. // example: // assume a DOM created by this markup: // | <span>append</span> // | <p>Hello Mars</p> // | <p>Hello World</p> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("span").appendTo("p"); // | }); // Results in this DOM structure: // | <p>Hello Mars<span>append</span></p> // | <p>Hello World<span>append</span></p> return this._placeMultiple(query, "last"); // dojo/NodeList }, prepend: function(/*String||DOMNode||NodeList*/content){ // summary: // prepends the content to every node in the NodeList. // description: // The content will be cloned if the length of NodeList // is greater than 1. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the appended content. // assume a DOM created by this markup: // | <div id="foo"><p>Hello Mars</p></div> // | <div id="bar"><p>Hello World</p></div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("div").prepend("<span>prepend</span>"); // | }); // Results in this DOM structure: // | <div id="foo"><span>prepend</span><p>Hello Mars</p></div> // | <div id="bar"><span>prepend</span><p>Hello World</p></div> return this.addContent(content, "first"); // dojo/NodeList }, prependTo: function(/*String*/query){ // summary: // prepends nodes in this NodeList to the nodes matched by // the query passed to prependTo. // description: // The nodes in this NodeList will be cloned if the query // matches more than one element. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the matched nodes from the query. // example: // assume a DOM created by this markup: // | <span>prepend</span> // | <p>Hello Mars</p> // | <p>Hello World</p> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("span").prependTo("p"); // | }); // Results in this DOM structure: // | <p><span>prepend</span>Hello Mars</p> // | <p><span>prepend</span>Hello World</p> return this._placeMultiple(query, "first"); // dojo/NodeList }, after: function(/*String||Element||NodeList*/content){ // summary: // Places the content after every node in the NodeList. // description: // The content will be cloned if the length of NodeList // is greater than 1. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the appended content. // example: // assume a DOM created by this markup: // | <div id="foo"><p>Hello Mars</p></div> // | <div id="bar"><p>Hello World</p></div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("div").after("<span>after</span>"); // | }); // Results in this DOM structure: // | <div id="foo"><p>Hello Mars</p></div><span>after</span> // | <div id="bar"><p>Hello World</p></div><span>after</span> return this.addContent(content, "after"); // dojo/NodeList }, insertAfter: function(/*String*/query){ // summary: // The nodes in this NodeList will be placed after the nodes // matched by the query passed to insertAfter. // description: // The nodes in this NodeList will be cloned if the query // matches more than one element. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the matched nodes from the query. // example: // assume a DOM created by this markup: // | <span>after</span> // | <p>Hello Mars</p> // | <p>Hello World</p> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("span").insertAfter("p"); // | }); // Results in this DOM structure: // | <p>Hello Mars</p><span>after</span> // | <p>Hello World</p><span>after</span> return this._placeMultiple(query, "after"); // dojo/NodeList }, before: function(/*String||DOMNode||NodeList*/content){ // summary: // Places the content before every node in the NodeList. // description: // The content will be cloned if the length of NodeList // is greater than 1. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the appended content. // example: // assume a DOM created by this markup: // | <div id="foo"><p>Hello Mars</p></div> // | <div id="bar"><p>Hello World</p></div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("div").before("<span>before</span>"); // | }); // Results in this DOM structure: // | <span>before</span><div id="foo"><p>Hello Mars</p></div> // | <span>before</span><div id="bar"><p>Hello World</p></div> return this.addContent(content, "before"); // dojo/NodeList }, insertBefore: function(/*String*/query){ // summary: // The nodes in this NodeList will be placed after the nodes // matched by the query passed to insertAfter. // description: // The nodes in this NodeList will be cloned if the query // matches more than one element. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // dojo/NodeList, the nodes currently in this NodeList will be returned, // not the matched nodes from the query. // example: // assume a DOM created by this markup: // | <span>before</span> // | <p>Hello Mars</p> // | <p>Hello World</p> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("span").insertBefore("p"); // | }); // Results in this DOM structure: // | <span>before</span><p>Hello Mars</p> // | <span>before</span><p>Hello World</p> return this._placeMultiple(query, "before"); // dojo/NodeList }, /*===== remove: function(simpleFilter){ // summary: // alias for dojo/NodeList's orphan method. Removes elements // in this list that match the simple filter from their parents // and returns them as a new NodeList. // simpleFilter: String // single-expression CSS rule. For example, ".thinger" or // "#someId[attrName='value']" but not "div > span". In short, // anything which does not invoke a descent to evaluate but // can instead be used to test a single node is acceptable. return; // dojo/NodeList }, =====*/ remove: NodeList.prototype.orphan, wrap: function(/*String||DOMNode*/html){ // summary: // Wrap each node in the NodeList with html passed to wrap. // description: // html will be cloned if the NodeList has more than one // element. Only DOM nodes are cloned, not any attached // event handlers. // returns: // the nodes in the current NodeList will be returned, // not the nodes from html argument. // example: // assume a DOM created by this markup: // | <b>one</b> // | <b>two</b> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query("b").wrap("<div><span></span></div>"); // | }); // Results in this DOM structure: // | <div><span><b>one</b></span></div> // | <div><span><b>two</b></span></div> if(this[0]){ html = makeWrapNode(html, this[0]); //Now cycle through the elements and do the insertion. for(var i = 0, node; node = this[i]; i++){ //Always clone because if html is used to hold one of //the "this" nodes, then on the clone of html it will contain //that "this" node, and that would be bad. var clone = this._cloneNode(html); if(node.parentNode){ node.parentNode.replaceChild(clone, node); } //Find deepest element and insert old node in it. var insertion = getWrapInsertion(clone); insertion.appendChild(node); } } return this; // dojo/NodeList }, wrapAll: function(/*String||DOMNode*/html){ // summary: // Insert html where the first node in this NodeList lives, then place all // nodes in this NodeList as the child of the html. // returns: // the nodes in the current NodeList will be returned, // not the nodes from html argument. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query(".red").wrapAll('<div class="allRed"></div>'); // | }); // Results in this DOM structure: // | <div class="container"> // | <div class="allRed"> // | <div class="red">Red One</div> // | <div class="red">Red Two</div> // | </div> // | <div class="blue">Blue One</div> // | <div class="blue">Blue Two</div> // | </div> if(this[0]){ html = makeWrapNode(html, this[0]); //Place the wrap HTML in place of the first node. this[0].parentNode.replaceChild(html, this[0]); //Now cycle through the elements and move them inside //the wrap. var insertion = getWrapInsertion(html); for(var i = 0, node; node = this[i]; i++){ insertion.appendChild(node); } } return this; // dojo/NodeList }, wrapInner: function(/*String||DOMNode*/html){ // summary: // For each node in the NodeList, wrap all its children with the passed in html. // description: // html will be cloned if the NodeList has more than one // element. Only DOM nodes are cloned, not any attached // event handlers. // returns: // the nodes in the current NodeList will be returned, // not the nodes from html argument. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query(".red").wrapInner('<span class="special"></span>'); // | }); // Results in this DOM structure: // | <div class="container"> // | <div class="red"><span class="special">Red One</span></div> // | <div class="blue">Blue One</div> // | <div class="red"><span class="special">Red Two</span></div> // | <div class="blue">Blue Two</div> // | </div> if(this[0]){ html = makeWrapNode(html, this[0]); for(var i = 0; i < this.length; i++){ //Always clone because if html is used to hold one of //the "this" nodes, then on the clone of html it will contain //that "this" node, and that would be bad. var clone = this._cloneNode(html); //Need to convert the childNodes to an array since wrapAll modifies the //DOM and can change the live childNodes NodeList. this._wrap(lang._toArray(this[i].childNodes), null, this._NodeListCtor).wrapAll(clone); } } return this; // dojo/NodeList }, replaceWith: function(/*String||DOMNode||NodeList*/content){ // summary: // Replaces each node in ths NodeList with the content passed to replaceWith. // description: // The content will be cloned if the length of NodeList // is greater than 1. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // The nodes currently in this NodeList will be returned, not the replacing content. // Note that the returned nodes have been removed from the DOM. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query(".red").replaceWith('<div class="green">Green</div>'); // | }); // Results in this DOM structure: // | <div class="container"> // | <div class="green">Green</div> // | <div class="blue">Blue One</div> // | <div class="green">Green</div> // | <div class="blue">Blue Two</div> // | </div> content = this._normalize(content, this[0]); for(var i = 0, node; node = this[i]; i++){ this._place(content, node, "before", i > 0); node.parentNode.removeChild(node); } return this; // dojo/NodeList }, replaceAll: function(/*String*/query){ // summary: // replaces nodes matched by the query passed to replaceAll with the nodes // in this NodeList. // description: // The nodes in this NodeList will be cloned if the query // matches more than one element. Only the DOM nodes are cloned, not // any attached event handlers. // returns: // The nodes currently in this NodeList will be returned, not the matched nodes // from the query. The nodes currently in this NodeLIst could have // been cloned, so the returned NodeList will include the cloned nodes. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="spacer">___</div> // | <div class="red">Red One</div> // | <div class="spacer">___</div> // | <div class="blue">Blue One</div> // | <div class="spacer">___</div> // | <div class="red">Red Two</div> // | <div class="spacer">___</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query(".red").replaceAll(".blue"); // | }); // Results in this DOM structure: // | <div class="container"> // | <div class="spacer">___</div> // | <div class="spacer">___</div> // | <div class="red">Red One</div> // | <div class="red">Red Two</div> // | <div class="spacer">___</div> // | <div class="spacer">___</div> // | <div class="red">Red One</div> // | <div class="red">Red Two</div> // | </div> var nl = dquery(query); var content = this._normalize(this, this[0]); for(var i = 0, node; node = nl[i]; i++){ this._place(content, node, "before", i > 0); node.parentNode.removeChild(node); } return this; // dojo/NodeList }, clone: function(){ // summary: // Clones all the nodes in this NodeList and returns them as a new NodeList. // description: // Only the DOM nodes are cloned, not any attached event handlers. // returns: // a cloned set of the original nodes. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-manipulate" // | ], function(query){ // | query(".red").clone().appendTo(".container"); // | }); // Results in this DOM structure: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | <div class="red">Red One</div> // | <div class="red">Red Two</div> // | </div> //TODO: need option to clone events? var ary = []; for(var i = 0; i < this.length; i++){ ary.push(this._cloneNode(this[i])); } return this._wrap(ary, this, this._NodeListCtor); // dojo/NodeList } }); //set up html method if one does not exist if(!NodeList.prototype.html){ NodeList.prototype.html = NodeList.prototype.innerHTML; } return NodeList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | 1 | define(["./query", "./_base/lang", "./_base/array"], function(dquery, lang, array){ // module: // dojo/NodeList-traverse /*===== return function(){ // summary: // Adds chainable methods to dojo/query() / NodeList instances for traversing the DOM }; =====*/ var NodeList = dquery.NodeList; lang.extend(NodeList, { _buildArrayFromCallback: function(/*Function*/ callback){ // summary: // builds a new array of possibly differing size based on the input list. // Since the returned array is likely of different size than the input array, // the array's map function cannot be used. var ary = []; for(var i = 0; i < this.length; i++){ var items = callback.call(this[i], this[i], ary); if(items){ ary = ary.concat(items); } } return ary; //Array }, _getUniqueAsNodeList: function(/*Array*/ nodes){ // summary: // given a list of nodes, make sure only unique // elements are returned as our NodeList object. // Does not call _stash(). var ary = []; //Using for loop for better speed. for(var i = 0, node; node = nodes[i]; i++){ //Should be a faster way to do this. dojo/query has a private //_zip function that may be inspirational, but there are pathways //in query that force nozip? if(node.nodeType == 1 && array.indexOf(ary, node) == -1){ ary.push(node); } } return this._wrap(ary, null, this._NodeListCtor); // dojo/NodeList }, _getUniqueNodeListWithParent: function(/*Array*/ nodes, /*String*/ query){ // summary: // gets unique element nodes, filters them further // with an optional query and then calls _stash to track parent NodeList. var ary = this._getUniqueAsNodeList(nodes); ary = (query ? dquery._filterResult(ary, query) : ary); return ary._stash(this); // dojo/NodeList }, _getRelatedUniqueNodes: function(/*String?*/ query, /*Function*/ callback){ // summary: // cycles over all the nodes and calls a callback // to collect nodes for a possible inclusion in a result. // The callback will get two args: callback(node, ary), // where ary is the array being used to collect the nodes. return this._getUniqueNodeListWithParent(this._buildArrayFromCallback(callback), query); // dojo/NodeList }, children: function(/*String?*/ query){ // summary: // Returns all immediate child elements for nodes in this dojo/NodeList. // Optionally takes a query to filter the child elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // all immediate child elements for the nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | Some Text // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".container").children(); // | }); // returns the four divs that are children of the container div. // Running this code: // | dojo.query(".container").children(".red"); // returns the two divs that have the class "red". return this._getRelatedUniqueNodes(query, function(node, ary){ return lang._toArray(node.childNodes); }); // dojo/NodeList }, closest: function(/*String*/ query, /*String|DOMNode?*/ root){ // summary: // Returns closest parent that matches query, including current node in this // dojo/NodeList if it matches the query. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // root: // If specified, query is relative to "root" rather than document body. // returns: // the closest parent that matches the query, including the current // node in this dojo/NodeList if it matches the query. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | Some Text // | <div class="blue">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".red").closest(".container"); // | }); // returns the div with class "container". return this._getRelatedUniqueNodes(null, function(node, ary){ do{ if(dquery._filterResult([node], query, root).length){ return node; } }while(node != root && (node = node.parentNode) && node.nodeType == 1); return null; //To make rhino strict checking happy. }); // dojo/NodeList }, parent: function(/*String?*/ query){ // summary: // Returns immediate parent elements for nodes in this dojo/NodeList. // Optionally takes a query to filter the parent elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // immediate parent elements for nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue first"><span class="text">Blue One</span></div> // | <div class="red">Red Two</div> // | <div class="blue"><span class="text">Blue Two</span></div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".text").parent(); // | }); // returns the two divs with class "blue". // Running this code: // | query(".text").parent(".first"); // returns the one div with class "blue" and "first". return this._getRelatedUniqueNodes(query, function(node, ary){ return node.parentNode; }); // dojo/NodeList }, parents: function(/*String?*/ query){ // summary: // Returns all parent elements for nodes in this dojo/NodeList. // Optionally takes a query to filter the child elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // all parent elements for nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue first"><span class="text">Blue One</span></div> // | <div class="red">Red Two</div> // | <div class="blue"><span class="text">Blue Two</span></div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".text").parents(); // | }); // returns the two divs with class "blue", the div with class "container", // | the body element and the html element. // Running this code: // | query(".text").parents(".container"); // returns the one div with class "container". return this._getRelatedUniqueNodes(query, function(node, ary){ var pary = []; while(node.parentNode){ node = node.parentNode; pary.push(node); } return pary; }); // dojo/NodeList }, siblings: function(/*String?*/ query){ // summary: // Returns all sibling elements for nodes in this dojo/NodeList. // Optionally takes a query to filter the sibling elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // all sibling elements for nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | Some Text // | <div class="blue first">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".first").siblings(); // | }); // returns the two divs with class "red" and the other div // | with class "blue" that does not have "first". // Running this code: // | query(".first").siblings(".red"); // returns the two div with class "red". return this._getRelatedUniqueNodes(query, function(node, ary){ var pary = []; var nodes = (node.parentNode && node.parentNode.childNodes); for(var i = 0; i < nodes.length; i++){ if(nodes[i] != node){ pary.push(nodes[i]); } } return pary; }); // dojo/NodeList }, next: function(/*String?*/ query){ // summary: // Returns the next element for nodes in this dojo/NodeList. // Optionally takes a query to filter the next elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // the next element for nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | Some Text // | <div class="blue first">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue last">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".first").next(); // | }); // returns the div with class "red" and has innerHTML of "Red Two". // Running this code: // | dojo.query(".last").next(".red"); // does not return any elements. return this._getRelatedUniqueNodes(query, function(node, ary){ var next = node.nextSibling; while(next && next.nodeType != 1){ next = next.nextSibling; } return next; }); // dojo/NodeList }, nextAll: function(/*String?*/ query){ // summary: // Returns all sibling elements that come after the nodes in this dojo/NodeList. // Optionally takes a query to filter the sibling elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // all sibling elements that come after the nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | Some Text // | <div class="blue first">Blue One</div> // | <div class="red next">Red Two</div> // | <div class="blue next">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".first").nextAll(); // | }); // returns the two divs with class of "next". // Running this code: // | query(".first").nextAll(".red"); // returns the one div with class "red" and innerHTML "Red Two". return this._getRelatedUniqueNodes(query, function(node, ary){ var pary = []; var next = node; while((next = next.nextSibling)){ if(next.nodeType == 1){ pary.push(next); } } return pary; }); // dojo/NodeList }, prev: function(/*String?*/ query){ // summary: // Returns the previous element for nodes in this dojo/NodeList. // Optionally takes a query to filter the previous elements. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // the previous element for nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | Some Text // | <div class="blue first">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".first").prev(); // | }); // returns the div with class "red" and has innerHTML of "Red One". // Running this code: // | query(".first").prev(".blue"); // does not return any elements. return this._getRelatedUniqueNodes(query, function(node, ary){ var prev = node.previousSibling; while(prev && prev.nodeType != 1){ prev = prev.previousSibling; } return prev; }); // dojo/NodeList }, prevAll: function(/*String?*/ query){ // summary: // Returns all sibling elements that come before the nodes in this dojo/NodeList. // Optionally takes a query to filter the sibling elements. // description: // The returned nodes will be in reverse DOM order -- the first node in the list will // be the node closest to the original node/NodeList. // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // query: // a CSS selector. // returns: // all sibling elements that come before the nodes in this dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red prev">Red One</div> // | Some Text // | <div class="blue prev">Blue One</div> // | <div class="red second">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".second").prevAll(); // | }); // returns the two divs with class of "prev". // Running this code: // | query(".first").prevAll(".red"); // returns the one div with class "red prev" and innerHTML "Red One". return this._getRelatedUniqueNodes(query, function(node, ary){ var pary = []; var prev = node; while((prev = prev.previousSibling)){ if(prev.nodeType == 1){ pary.push(prev); } } return pary; }); // dojo/NodeList }, andSelf: function(){ // summary: // Adds the nodes from the previous dojo/NodeList to the current dojo/NodeList. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red prev">Red One</div> // | Some Text // | <div class="blue prev">Blue One</div> // | <div class="red second">Red Two</div> // | <div class="blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".second").prevAll().andSelf(); // | }); // returns the two divs with class of "prev", as well as the div with class "second". return this.concat(this._parent); // dojo/NodeList }, //Alternate methods for the :first/:last/:even/:odd pseudos. first: function(){ // summary: // Returns the first node in this dojo/NodeList as a dojo/NodeList. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // returns: // the first node in this dojo/NodeList // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue first">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue last">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".blue").first(); // | }); // returns the div with class "blue" and "first". return this._wrap(((this[0] && [this[0]]) || []), this); // dojo/NodeList }, last: function(){ // summary: // Returns the last node in this dojo/NodeList as a dojo/NodeList. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // returns: // the last node in this dojo/NodeList // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="red">Red One</div> // | <div class="blue first">Blue One</div> // | <div class="red">Red Two</div> // | <div class="blue last">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".blue").last(); // | }); // returns the last div with class "blue", return this._wrap((this.length ? [this[this.length - 1]] : []), this); // dojo/NodeList }, even: function(){ // summary: // Returns the even nodes in this dojo/NodeList as a dojo/NodeList. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // returns: // the even nodes in this dojo/NodeList // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="interior red">Red One</div> // | <div class="interior blue">Blue One</div> // | <div class="interior red">Red Two</div> // | <div class="interior blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".interior").even(); // | }); // returns the two divs with class "blue" return this.filter(function(item, i){ return i % 2 != 0; }); // dojo/NodeList }, odd: function(){ // summary: // Returns the odd nodes in this dojo/NodeList as a dojo/NodeList. // description: // .end() can be used on the returned dojo/NodeList to get back to the // original dojo/NodeList. // returns: // the odd nodes in this dojo/NodeList // example: // assume a DOM created by this markup: // | <div class="container"> // | <div class="interior red">Red One</div> // | <div class="interior blue">Blue One</div> // | <div class="interior red">Red Two</div> // | <div class="interior blue">Blue Two</div> // | </div> // Running this code: // | require(["dojo/query", "dojo/NodeList-traverse" // | ], function(query){ // | query(".interior").odd(); // | }); // returns the two divs with class "red" return this.filter(function(item, i){ return i % 2 == 0; }); // dojo/NodeList } }); return NodeList; }); |
| 1 2 3 4 5 6 7 8 | 1 | define(["./query"], function(query){ // This class is just for documentation purposes, so NodeList shows up well in the API viewer, // and to simplify writing API doc for all the methods that take NodeList as a parameter, or return a NodeList. return query.NodeList; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | 2 | /*******************************************************************************
* OpenAjax.js
*
* Reference implementation of the OpenAjax Hub, as specified by OpenAjax Alliance.
* Specification is under development at:
*
* http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
*
* Copyright 2006-2007 OpenAjax Alliance
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0 . Unless
* required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
******************************************************************************/
// prevent re-definition of the OpenAjax object
if(!window["OpenAjax"]){
OpenAjax = new function(){
// summary:
// the OpenAjax hub
// description:
// see http://www.openajax.org/member/wiki/OpenAjax_Hub_Specification
var libs = {};
var ooh = "org.openajax.hub.";
var h = {};
this.hub = h;
h.implementer = "http://openajax.org";
h.implVersion = "0.6";
h.specVersion = "0.6";
h.implExtraData = {};
h.libraries = libs;
h.registerLibrary = function(prefix, nsURL, version, extra){
libs[prefix] = {
prefix: prefix,
namespaceURI: nsURL,
version: version,
extraData: extra
};
this.publish(ooh+"registerLibrary", libs[prefix]);
};
h.unregisterLibrary = function(prefix){
this.publish(ooh+"unregisterLibrary", libs[prefix]);
delete libs[prefix];
};
h._subscriptions = { c:{}, s:[] };
h._cleanup = [];
h._subIndex = 0;
h._pubDepth = 0;
h.subscribe = function(name, callback, scope, subscriberData, filter){
if(!scope){
scope = window;
}
var handle = name + "." + this._subIndex;
var sub = { scope: scope, cb: callback, fcb: filter, data: subscriberData, sid: this._subIndex++, hdl: handle };
var path = name.split(".");
this._subscribe(this._subscriptions, path, 0, sub);
return handle;
};
h.publish = function(name, message){
var path = name.split(".");
this._pubDepth++;
this._publish(this._subscriptions, path, 0, name, message);
this._pubDepth--;
if((this._cleanup.length > 0) && (this._pubDepth == 0)){
for(var i = 0; i < this._cleanup.length; i++){
this.unsubscribe(this._cleanup[i].hdl);
}
delete(this._cleanup);
this._cleanup = [];
}
};
h.unsubscribe = function(sub){
var path = sub.split(".");
var sid = path.pop();
this._unsubscribe(this._subscriptions, path, 0, sid);
};
h._subscribe = function(tree, path, index, sub){
var token = path[index];
if(index == path.length){
tree.s.push(sub);
}else{
if(typeof tree.c == "undefined"){
tree.c = {};
}
if(typeof tree.c[token] == "undefined"){
tree.c[token] = { c: {}, s: [] };
}
this._subscribe(tree.c[token], path, index + 1, sub);
}
};
h._publish = function(tree, path, index, name, msg){
if(typeof tree != "undefined"){
var node;
if(index == path.length){
node = tree;
}else{
this._publish(tree.c[path[index]], path, index + 1, name, msg);
this._publish(tree.c["*"], path, index + 1, name, msg);
node = tree.c["**"];
}
if(typeof node != "undefined"){
var callbacks = node.s;
var max = callbacks.length;
for(var i = 0; i < max; i++){
if(callbacks[i].cb){
var sc = callbacks[i].scope;
var cb = callbacks[i].cb;
var fcb = callbacks[i].fcb;
var d = callbacks[i].data;
if(typeof cb == "string"){
// get a function object
cb = sc[cb];
}
if(typeof fcb == "string"){
// get a function object
fcb = sc[fcb];
}
if((!fcb) ||
(fcb.call(sc, name, msg, d))){
cb.call(sc, name, msg, d);
}
}
}
}
}
};
h._unsubscribe = function(tree, path, index, sid){
if(typeof tree != "undefined"){
if(index < path.length){
var childNode = tree.c[path[index]];
this._unsubscribe(childNode, path, index + 1, sid);
if(childNode.s.length == 0){
for(var x in childNode.c)
return;
delete tree.c[path[index]];
}
return;
}
else{
var callbacks = tree.s;
var max = callbacks.length;
for(var i = 0; i < max; i++){
if(sid == callbacks[i].sid){
if(this._pubDepth > 0){
callbacks[i].cb = null;
this._cleanup.push(callbacks[i]);
}
else
callbacks.splice(i, 1);
return;
}
}
}
}
};
// The following function is provided for automatic testing purposes.
// It is not expected to be deployed in run-time OpenAjax Hub implementations.
h.reinit = function(){
for (var lib in OpenAjax.hub.libraries){
delete OpenAjax.hub.libraries[lib];
}
OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "0.6", {});
delete OpenAjax._subscriptions;
OpenAjax._subscriptions = {c:{},s:[]};
delete OpenAjax._cleanup;
OpenAjax._cleanup = [];
OpenAjax._subIndex = 0;
OpenAjax._pubDepth = 0;
};
};
// Register the OpenAjax Hub itself as a library.
OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "0.6", {});
}
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | 1 | define(["./_base/declare", "./_base/lang", "./_base/array", "./when"], function(declare, lang, array, when){ // module: // dojo/Stateful return declare("dojo.Stateful", null, { // summary: // Base class for objects that provide named properties with optional getter/setter // control and the ability to watch for property changes // // The class also provides the functionality to auto-magically manage getters // and setters for object attributes/properties. // // Getters and Setters should follow the format of _xxxGetter or _xxxSetter where // the xxx is a name of the attribute to handle. So an attribute of "foo" // would have a custom getter of _fooGetter and a custom setter of _fooSetter. // // example: // | require(["dojo/Stateful", function(Stateful) { // | var obj = new Stateful(); // | obj.watch("foo", function(){ // | console.log("foo changed to " + this.get("foo")); // | }); // | obj.set("foo","bar"); // | }); // _attrPairNames: Hash // Used across all instances a hash to cache attribute names and their getter // and setter names. _attrPairNames: {}, _getAttrNames: function(name){ // summary: // Helper function for get() and set(). // Caches attribute name values so we don't do the string ops every time. // tags: // private var apn = this._attrPairNames; if(apn[name]){ return apn[name]; } return (apn[name] = { s: "_" + name + "Setter", g: "_" + name + "Getter" }); }, postscript: function(/*Object?*/ params){ // Automatic setting of params during construction if (params){ this.set(params); } }, _get: function(name, names){ // summary: // Private function that does a get based off a hash of names // names: // Hash of names of custom attributes return typeof this[names.g] === "function" ? this[names.g]() : this[name]; }, get: function(/*String*/name){ // summary: // Get a property on a Stateful instance. // name: // The property to get. // returns: // The property value on this Stateful instance. // description: // Get a named property on a Stateful object. The property may // potentially be retrieved via a getter method in subclasses. In the base class // this just retrieves the object's property. // example: // | require(["dojo/Stateful", function(Stateful) { // | var stateful = new Stateful({foo: 3}); // | stateful.get("foo") // returns 3 // | stateful.foo // returns 3 // | }); return this._get(name, this._getAttrNames(name)); //Any }, set: function(/*String*/name, /*Object*/value){ // summary: // Set a property on a Stateful instance // name: // The property to set. // value: // The value to set in the property. // returns: // The function returns this dojo.Stateful instance. // description: // Sets named properties on a stateful object and notifies any watchers of // the property. A programmatic setter may be defined in subclasses. // example: // | require(["dojo/Stateful", function(Stateful) { // | var stateful = new Stateful(); // | stateful.watch(function(name, oldValue, value){ // | // this will be called on the set below // | } // | stateful.set(foo, 5); // set() may also be called with a hash of name/value pairs, ex: // | stateful.set({ // | foo: "Howdy", // | bar: 3 // | }); // | }); // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) // If an object is used, iterate through object if(typeof name === "object"){ for(var x in name){ if(name.hasOwnProperty(x) && x !="_watchCallbacks"){ this.set(x, name[x]); } } return this; } var names = this._getAttrNames(name), oldValue = this._get(name, names), setter = this[names.s], result; if(typeof setter === "function"){ // use the explicit setter result = setter.apply(this, Array.prototype.slice.call(arguments, 1)); }else{ // no setter so set attribute directly this[name] = value; } if(this._watchCallbacks){ var self = this; // If setter returned a promise, wait for it to complete, otherwise call watches immediatly when(result, function(){ self._watchCallbacks(name, oldValue, value); }); } return this; // dojo/Stateful }, _changeAttrValue: function(name, value){ // summary: // Internal helper for directly changing an attribute value. // // name: String // The property to set. // value: Mixed // The value to set in the property. // // description: // Directly change the value of an attribute on an object, bypassing any // accessor setter. Also handles the calling of watch and emitting events. // It is designed to be used by descendent class when there are two values // of attributes that are linked, but calling .set() is not appropriate. var oldValue = this.get(name); this[name] = value; if(this._watchCallbacks){ this._watchCallbacks(name, oldValue, value); } return this; // dojo/Stateful }, watch: function(/*String?*/name, /*Function*/callback){ // summary: // Watches a property for changes // name: // Indicates the property to watch. This is optional (the callback may be the // only parameter), and if omitted, all the properties will be watched // returns: // An object handle for the watch. The unwatch method of this object // can be used to discontinue watching this property: // | var watchHandle = obj.watch("foo", callback); // | watchHandle.unwatch(); // callback won't be called now // callback: // The function to execute when the property changes. This will be called after // the property has been changed. The callback will be called with the |this| // set to the instance, the first argument as the name of the property, the // second argument as the old value and the third argument as the new value. var callbacks = this._watchCallbacks; if(!callbacks){ var self = this; callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){ var notify = function(propertyCallbacks){ if(propertyCallbacks){ propertyCallbacks = propertyCallbacks.slice(); for(var i = 0, l = propertyCallbacks.length; i < l; i++){ propertyCallbacks[i].call(self, name, oldValue, value); } } }; notify(callbacks['_' + name]); if(!ignoreCatchall){ notify(callbacks["*"]); // the catch-all } }; // we use a function instead of an object so it will be ignored by JSON conversion } if(!callback && typeof name === "function"){ callback = name; name = "*"; }else{ // prepend with dash to prevent name conflicts with function (like "name" property) name = '_' + name; } var propertyCallbacks = callbacks[name]; if(typeof propertyCallbacks !== "object"){ propertyCallbacks = callbacks[name] = []; } propertyCallbacks.push(callback); // TODO: Remove unwatch in 2.0 var handle = {}; handle.unwatch = handle.remove = function(){ var index = array.indexOf(propertyCallbacks, callback); if(index > -1){ propertyCallbacks.splice(index, 1); } }; return handle; //Object } }); }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | 2 1 1 | define([], function(){ // module: // dojo/aspect "use strict"; var undefined, nextId = 0; function advise(dispatcher, type, advice, receiveArguments){ var previous = dispatcher[type]; var around = type == "around"; var signal; if(around){ var advised = advice(function(){ return previous.advice(this, arguments); }); signal = { remove: function(){ if(advised){ advised = dispatcher = advice = null; } }, advice: function(target, args){ return advised ? advised.apply(target, args) : // called the advised function previous.advice(target, args); // cancelled, skip to next one } }; }else{ // create the remove handler signal = { remove: function(){ if(signal.advice){ var previous = signal.previous; var next = signal.next; if(!next && !previous){ delete dispatcher[type]; }else{ if(previous){ previous.next = next; }else{ dispatcher[type] = next; } if(next){ next.previous = previous; } } // remove the advice to signal that this signal has been removed dispatcher = advice = signal.advice = null; } }, id: nextId++, advice: advice, receiveArguments: receiveArguments }; } if(previous && !around){ if(type == "after"){ // add the listener to the end of the list // note that we had to change this loop a little bit to workaround a bizarre IE10 JIT bug while(previous.next && (previous = previous.next)){} previous.next = signal; signal.previous = previous; }else if(type == "before"){ // add to beginning dispatcher[type] = signal; signal.next = previous; previous.previous = signal; } }else{ // around or first one just replaces dispatcher[type] = signal; } return signal; } function aspect(type){ return function(target, methodName, advice, receiveArguments){ var existing = target[methodName], dispatcher; if(!existing || existing.target != target){ // no dispatcher in place target[methodName] = dispatcher = function(){ var executionId = nextId; // before advice var args = arguments; var before = dispatcher.before; while(before){ args = before.advice.apply(this, args) || args; before = before.next; } // around advice if(dispatcher.around){ var results = dispatcher.around.advice(this, args); } // after advice var after = dispatcher.after; while(after && after.id < executionId){ if(after.receiveArguments){ var newResults = after.advice.apply(this, args); // change the return value only if a new value was returned results = newResults === undefined ? results : newResults; }else{ results = after.advice.call(this, results, args); } after = after.next; } return results; }; if(existing){ dispatcher.around = {advice: function(target, args){ return existing.apply(target, args); }}; } dispatcher.target = target; } var results = advise((dispatcher || existing), type, advice, receiveArguments); advice = null; return results; }; } // TODOC: after/before/around return object var after = aspect("after"); /*===== after = function(target, methodName, advice, receiveArguments){ // summary: // The "after" export of the aspect module is a function that can be used to attach // "after" advice to a method. This function will be executed after the original method // is executed. By default the function will be called with a single argument, the return // value of the original method, or the the return value of the last executed advice (if a previous one exists). // The fourth (optional) argument can be set to true to so the function receives the original // arguments (from when the original method was called) rather than the return value. // If there are multiple "after" advisors, they are executed in the order they were registered. // target: Object // This is the target object // methodName: String // This is the name of the method to attach to. // advice: Function // This is function to be called after the original method // receiveArguments: Boolean? // If this is set to true, the advice function receives the original arguments (from when the original mehtod // was called) rather than the return value of the original/previous method. // returns: // A signal object that can be used to cancel the advice. If remove() is called on this signal object, it will // stop the advice function from being executed. }; =====*/ var before = aspect("before"); /*===== before = function(target, methodName, advice){ // summary: // The "before" export of the aspect module is a function that can be used to attach // "before" advice to a method. This function will be executed before the original method // is executed. This function will be called with the arguments used to call the method. // This function may optionally return an array as the new arguments to use to call // the original method (or the previous, next-to-execute before advice, if one exists). // If the before method doesn't return anything (returns undefined) the original arguments // will be preserved. // If there are multiple "before" advisors, they are executed in the reverse order they were registered. // target: Object // This is the target object // methodName: String // This is the name of the method to attach to. // advice: Function // This is function to be called before the original method }; =====*/ var around = aspect("around"); /*===== around = function(target, methodName, advice){ // summary: // The "around" export of the aspect module is a function that can be used to attach // "around" advice to a method. The advisor function is immediately executed when // the around() is called, is passed a single argument that is a function that can be // called to continue execution of the original method (or the next around advisor). // The advisor function should return a function, and this function will be called whenever // the method is called. It will be called with the arguments used to call the method. // Whatever this function returns will be returned as the result of the method call (unless after advise changes it). // example: // If there are multiple "around" advisors, the most recent one is executed first, // which can then delegate to the next one and so on. For example: // | around(obj, "foo", function(originalFoo){ // | return function(){ // | var start = new Date().getTime(); // | var results = originalFoo.apply(this, arguments); // call the original // | var end = new Date().getTime(); // | console.log("foo execution took " + (end - start) + " ms"); // | return results; // | }; // | }); // target: Object // This is the target object // methodName: String // This is the name of the method to attach to. // advice: Function // This is function to be called around the original method }; =====*/ return { // summary: // provides aspect oriented programming functionality, allowing for // one to add before, around, or after advice on existing methods. // example: // | define(["dojo/aspect"], function(aspect){ // | var signal = aspect.after(targetObject, "methodName", function(someArgument){ // | this will be called when targetObject.methodName() is called, after the original function is called // | }); // // example: // The returned signal object can be used to cancel the advice. // | signal.remove(); // this will stop the advice from being executed anymore // | aspect.before(targetObject, "methodName", function(someArgument){ // | // this will be called when targetObject.methodName() is called, before the original function is called // | }); before: before, around: around, after: after }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | 2 1 1 1 1 1 1 | define(["./_base/config", "./_base/lang", "./sniff", "./dom", "./dom-construct", "./_base/window", "require"], function(config, lang, has, dom, domConstruct, baseWindow, require){ // module: // dojo/back var back = { // summary: // Browser history management resources }; has("extend-dojo") && lang.setObject("dojo.back", back); // everyone deals with encoding the hash slightly differently var getHash = back.getHash = function(){ var h = window.location.hash; if(h.charAt(0) == "#"){ h = h.substring(1); } return has("mozilla") ? h : decodeURIComponent(h); }, setHash = back.setHash = function(h){ if(!h){ h = ""; } window.location.hash = encodeURIComponent(h); historyCounter = history.length; }; var initialHref = (typeof(window) !== "undefined") ? window.location.href : ""; var initialHash = (typeof(window) !== "undefined") ? getHash() : ""; var initialState = null; var locationTimer = null; var bookmarkAnchor = null; var historyIframe = null; var forwardStack = []; var historyStack = []; var moveForward = false; var changingUrl = false; var historyCounter; function handleBackButton(){ // summary: // private method. Do not call this directly. //The "current" page is always at the top of the history stack. var current = historyStack.pop(); if(!current){ return; } var last = historyStack[historyStack.length-1]; if(!last && historyStack.length == 0){ last = initialState; } if(last){ if(last.kwArgs["back"]){ last.kwArgs["back"](); }else if(last.kwArgs["backButton"]){ last.kwArgs["backButton"](); }else if(last.kwArgs["handle"]){ last.kwArgs.handle("back"); } } forwardStack.push(current); } back.goBack = handleBackButton; function handleForwardButton(){ // summary: // private method. Do not call this directly. var last = forwardStack.pop(); if(!last){ return; } if(last.kwArgs["forward"]){ last.kwArgs.forward(); }else if(last.kwArgs["forwardButton"]){ last.kwArgs.forwardButton(); }else if(last.kwArgs["handle"]){ last.kwArgs.handle("forward"); } historyStack.push(last); } back.goForward = handleForwardButton; function createState(url, args, hash){ // summary: // private method. Do not call this directly. return {"url": url, "kwArgs": args, "urlHash": hash}; //Object } function getUrlQuery(url){ // summary: // private method. Do not call this directly. var segments = url.split("?"); if(segments.length < 2){ return null; //null } else{ return segments[1]; //String } } function loadIframeHistory(){ // summary: // private method. Do not call this directly. var url = (config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html")) + "?" + (new Date()).getTime(); moveForward = true; if(historyIframe){ has("webkit") ? historyIframe.location = url : window.frames[historyIframe.name].location = url; }else{ //console.warn("dojo/back: Not initialised. You need to call back.init() from a <script> block that lives inside the <body> tag."); } return url; //String } function checkLocation(){ if(!changingUrl){ var hsl = historyStack.length; var hash = getHash(); if((hash === initialHash||window.location.href == initialHref)&&(hsl == 1)){ // FIXME: could this ever be a forward button? // we can't clear it because we still need to check for forwards. Ugg. // clearInterval(this.locationTimer); handleBackButton(); return; } // first check to see if we could have gone forward. We always halt on // a no-hash item. if(forwardStack.length > 0){ if(forwardStack[forwardStack.length-1].urlHash === hash){ handleForwardButton(); return; } } // ok, that didn't work, try someplace back in the history stack if((hsl >= 2)&&(historyStack[hsl-2])){ if(historyStack[hsl-2].urlHash === hash){ handleBackButton(); } } } } back.init = function(){ // summary: // Initializes the undo stack. This must be called from a <script> // block that lives inside the `<body>` tag to prevent bugs on IE. // // Only call this method before the page's DOM is finished loading. Otherwise // it will not work. Be careful with xdomain loading or djConfig.debugAtAllCosts scenarios, // in order for this method to work, dojo/back will need to be part of a build layer. // prevent reinit if(dom.byId("dj_history")){ return; } var src = config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html"); if (config.afterOnLoad){ console.error("dojo/back::init() must be called before the DOM has loaded. " + "Include dojo/back in a build layer."); }else{ document.write('<iframe style="border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;" name="dj_history" id="dj_history" src="' + src + '"></iframe>'); } }; back.setInitialState = function(/*Object*/args){ // summary: // Sets the state object and back callback for the very first page // that is loaded. // // It is recommended that you call this method as part of an event // listener that is registered via dojo/ready. // args: Object // See the addToHistory() function for the list of valid args properties. initialState = createState(initialHref, args, initialHash); }; //FIXME: Make these doc comments not be awful. At least they're not wrong. //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things. //FIXME: is there a slight race condition in moz using change URL with the timer check and when // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent. /*===== var __backArgs = { // back: Function? // A function to be called when this state is reached via the user // clicking the back button. // forward: Function? // Upon return to this state from the "back, forward" combination // of navigation steps, this function will be called. Somewhat // analogous to the semantic of an "onRedo" event handler. // changeUrl: Boolean|String? // Boolean indicating whether or not to create a unique hash for // this state. If a string is passed instead, it is used as the // hash. }; =====*/ back.addToHistory = function(args){ // summary: // adds a state object (args) to the history list. // args: __backArgs // The state object that will be added to the history list. // description: // To support getting back button notifications, the object // argument should implement a function called either "back", // "backButton", or "handle". The string "back" will be passed as // the first and only argument to this callback. // // To support getting forward button notifications, the object // argument should implement a function called either "forward", // "forwardButton", or "handle". The string "forward" will be // passed as the first and only argument to this callback. // // If you want the browser location string to change, define "changeUrl" on the object. If the // value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment // identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does // not evaluate to false, that value will be used as the fragment identifier. For example, // if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1 // // There are problems with using dojo/back with semantically-named fragment identifiers // ("hash values" on an URL). In most browsers it will be hard for dojo/back to know // distinguish a back from a forward event in those cases. For back/forward support to // work best, the fragment ID should always be a unique value (something using new Date().getTime() // for example). If you want to detect hash changes using semantic fragment IDs, then // consider using dojo/hash instead (in Dojo 1.4+). // // example: // | back.addToHistory({ // | back: function(){ console.log('back pressed'); }, // | forward: function(){ console.log('forward pressed'); }, // | changeUrl: true // | }); // BROWSER NOTES: // Safari 1.2: // back button "works" fine, however it's not possible to actually // DETECT that you've moved backwards by inspecting window.location. // Unless there is some other means of locating. // FIXME: perhaps we can poll on history.length? // Safari 2.0.3+ (and probably 1.3.2+): // works fine, except when changeUrl is used. When changeUrl is used, // Safari jumps all the way back to whatever page was shown before // the page that uses dojo.undo.browser support. // IE 5.5 SP2: // back button behavior is macro. It does not move back to the // previous hash value, but to the last full page load. This suggests // that the iframe is the correct way to capture the back button in // these cases. // Don't test this page using local disk for MSIE. MSIE will not create // a history list for iframe_history.html if served from a file: URL. // The XML served back from the XHR tests will also not be properly // created if served from local disk. Serve the test pages from a web // server to test in that browser. // IE 6.0: // same behavior as IE 5.5 SP2 // Firefox 1.0+: // the back button will return us to the previous hash on the same // page, thereby not requiring an iframe hack, although we do then // need to run a timer to detect inter-page movement. //If addToHistory is called, then that means we prune the //forward stack -- the user went back, then wanted to //start a new forward path. forwardStack = []; var hash = null; var url = null; if(!historyIframe){ if(config["useXDomain"] && !config["dojoIframeHistoryUrl"]){ console.warn("dojo/back: When using cross-domain Dojo builds," + " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl" + " to the path on your domain to iframe_history.html"); } historyIframe = window.frames["dj_history"]; } if(!bookmarkAnchor){ bookmarkAnchor = domConstruct.create("a", {style: {display: "none"}}, baseWindow.body()); } if(args["changeUrl"]){ hash = ""+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime()); //If the current hash matches the new one, just replace the history object with //this new one. It doesn't make sense to track different state objects for the same //logical URL. This matches the browser behavior of only putting in one history //item no matter how many times you click on the same #hash link, at least in Firefox //and Safari, and there is no reliable way in those browsers to know if a #hash link //has been clicked on multiple times. So making this the standard behavior in all browsers //so that dojo/back's behavior is the same in all browsers. if(historyStack.length == 0 && initialState.urlHash == hash){ initialState = createState(url, args, hash); return; }else if(historyStack.length > 0 && historyStack[historyStack.length - 1].urlHash == hash){ historyStack[historyStack.length - 1] = createState(url, args, hash); return; } changingUrl = true; setTimeout(function(){ setHash(hash); changingUrl = false; }, 1); bookmarkAnchor.href = hash; if(has("ie")){ url = loadIframeHistory(); var oldCB = args["back"]||args["backButton"]||args["handle"]; //The function takes handleName as a parameter, in case the //callback we are overriding was "handle". In that case, //we will need to pass the handle name to handle. var tcb = function(handleName){ if(getHash() != ""){ setTimeout(function(){ setHash(hash); }, 1); } //Use apply to set "this" to args, and to try to avoid memory leaks. oldCB.apply(this, [handleName]); }; //Set interceptor function in the right place. if(args["back"]){ args.back = tcb; }else if(args["backButton"]){ args.backButton = tcb; }else if(args["handle"]){ args.handle = tcb; } var oldFW = args["forward"]||args["forwardButton"]||args["handle"]; //The function takes handleName as a parameter, in case the //callback we are overriding was "handle". In that case, //we will need to pass the handle name to handle. var tfw = function(handleName){ if(getHash() != ""){ setHash(hash); } if(oldFW){ // we might not actually have one //Use apply to set "this" to args, and to try to avoid memory leaks. oldFW.apply(this, [handleName]); } }; //Set interceptor function in the right place. if(args["forward"]){ args.forward = tfw; }else if(args["forwardButton"]){ args.forwardButton = tfw; }else if(args["handle"]){ args.handle = tfw; } }else if(!has("ie")){ // start the timer if(!locationTimer){ locationTimer = setInterval(checkLocation, 200); } } }else{ url = loadIframeHistory(); } historyStack.push(createState(url, args, hash)); }; back._iframeLoaded = function(evt, ifrLoc){ // summary: // private method. Do not call this directly. var query = getUrlQuery(ifrLoc.href); if(query == null){ // alert("iframeLoaded"); // we hit the end of the history, so we should go back if(historyStack.length == 1){ handleBackButton(); } return; } if(moveForward){ // we were expecting it, so it's not either a forward or backward movement moveForward = false; return; } //Check the back stack first, since it is more likely. //Note that only one step back or forward is supported. if(historyStack.length >= 2 && query == getUrlQuery(historyStack[historyStack.length-2].url)){ handleBackButton(); }else if(forwardStack.length > 0 && query == getUrlQuery(forwardStack[forwardStack.length-1].url)){ handleForwardButton(); } }; return back; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | 2 1 1 | define(["./_base/kernel", "./_base/lang", "./_base/array", "./_base/connect", "./query", "./domReady"], function(dojo, lang, darray, connect, query, domReady){ // module: // dojo/behavior dojo.deprecated("dojo.behavior", "Use dojo/on with event delegation (on.selector())"); var Behavior = function(){ // summary: // Deprecated. dojo/behavior's functionality can be achieved using event delegation using dojo/on // and on.selector(). // description: // A very simple, lightweight mechanism for applying code to // existing documents, based around `dojo/query` (CSS3 selectors) for node selection, // and a simple two-command API: `add()` and `apply()`; // // Behaviors apply to a given page, and are registered following the syntax // options described by `add()` to match nodes to actions, or "behaviors". // // Added behaviors are applied to the current DOM when .apply() is called, // matching only new nodes found since .apply() was last called. function arrIn(obj, name){ if(!obj[name]){ obj[name] = []; } return obj[name]; } var _inc = 0; function forIn(obj, scope, func){ var tmpObj = {}; for(var x in obj){ if(typeof tmpObj[x] == "undefined"){ if(!func){ scope(obj[x], x); }else{ func.call(scope, obj[x], x); } } } } // FIXME: need a better test so we don't exclude nightly Safari's! this._behaviors = {}; this.add = function(/* Object */behaviorObj){ // summary: // Add the specified behavior to the list of behaviors, ignoring existing // matches. // behaviorObj: Object // The behavior object that will be added to behaviors list. The behaviors // in the list will be applied the next time apply() is called. // description: // Add the specified behavior to the list of behaviors which will // be applied the next time apply() is called. Calls to add() for // an already existing behavior do not replace the previous rules, // but are instead additive. New nodes which match the rule will // have all add()-ed behaviors applied to them when matched. // // The "found" method is a generalized handler that's called as soon // as the node matches the selector. Rules for values that follow also // apply to the "found" key. // // The "on*" handlers are attached with `dojo.connect()`, using the // matching node // // If the value corresponding to the ID key is a function and not a // list, it's treated as though it was the value of "found". // // dojo/behavior.add() can be called any number of times before // the DOM is ready. `dojo/behavior.apply()` is called automatically // by `dojo.addOnLoad`, though can be called to re-apply previously added // behaviors anytime the DOM changes. // // There are a variety of formats permitted in the behaviorObject // // example: // Simple list of properties. "found" is special. "Found" is assumed if // no property object for a given selector, and property is a function. // // | behavior.add({ // | "#id": { // | "found": function(element){ // | // node match found // | }, // | "onclick": function(evt){ // | // register onclick handler for found node // | } // | }, // | "#otherid": function(element){ // | // assumes "found" with this syntax // | } // | }); // // example: // If property is a string, a dojo.publish will be issued on the channel: // // | behavior.add({ // | // topic.publish() whenever class="noclick" found on anchors // | "a.noclick": "/got/newAnchor", // | "div.wrapper": { // | "onclick": "/node/wasClicked" // | } // | }); // | topic.subscribe("/got/newAnchor", function(node){ // | // handle node finding when dojo/behavior.apply() is called, // | // provided a newly matched node is found. // | }); // // example: // Scoping can be accomplished by passing an object as a property to // a connection handle (on*): // // | behavior.add({ // | "#id": { // | // like calling dojo.hitch(foo,"bar"). execute foo.bar() in scope of foo // | "onmouseenter": { targetObj: foo, targetFunc: "bar" }, // | "onmouseleave": { targetObj: foo, targetFunc: "baz" } // | } // | }); // // example: // Behaviors match on CSS3 Selectors, powered by dojo/query. Example selectors: // // | behavior.add({ // | // match all direct descendants // | "#id4 > *": function(element){ // | // ... // | }, // | // | // match the first child node that's an element // | "#id4 > :first-child": { ... }, // | // | // match the last child node that's an element // | "#id4 > :last-child": { ... }, // | // | // all elements of type tagname // | "tagname": { // | // ... // | }, // | // | "tagname1 tagname2 tagname3": { // | // ... // | }, // | // | ".classname": { // | // ... // | }, // | // | "tagname.classname": { // | // ... // | } // | }); // forIn(behaviorObj, this, function(behavior, name){ var tBehavior = arrIn(this._behaviors, name); if(typeof tBehavior["id"] != "number"){ tBehavior.id = _inc++; } var cversion = []; tBehavior.push(cversion); if((lang.isString(behavior))||(lang.isFunction(behavior))){ behavior = { found: behavior }; } forIn(behavior, function(rule, ruleName){ arrIn(cversion, ruleName).push(rule); }); }); }; var _applyToNode = function(node, action, ruleSetName){ if(lang.isString(action)){ if(ruleSetName == "found"){ connect.publish(action, [ node ]); }else{ connect.connect(node, ruleSetName, function(){ connect.publish(action, arguments); }); } }else if(lang.isFunction(action)){ if(ruleSetName == "found"){ action(node); }else{ connect.connect(node, ruleSetName, action); } } }; this.apply = function(){ // summary: // Applies all currently registered behaviors to the document. // // description: // Applies all currently registered behaviors to the document, // taking care to ensure that only incremental updates are made // since the last time add() or apply() were called. // // If new matching nodes have been added, all rules in a behavior will be // applied to that node. For previously matched nodes, only // behaviors which have been added since the last call to apply() // will be added to the nodes. // // apply() is called once automatically by `dojo.addOnLoad`, so // registering behaviors with `dojo/behavior.add()` before the DOM is // ready is acceptable, provided the dojo.behavior module is ready. // // Calling appy() manually after manipulating the DOM is required // to rescan the DOM and apply newly .add()ed behaviors, or to match // nodes that match existing behaviors when those nodes are added to // the DOM. // forIn(this._behaviors, function(tBehavior, id){ query(id).forEach( function(elem){ var runFrom = 0; var bid = "_dj_behavior_"+tBehavior.id; if(typeof elem[bid] == "number"){ runFrom = elem[bid]; if(runFrom == (tBehavior.length)){ return; } } // run through the versions, applying newer rules at each step for(var x=runFrom, tver; tver = tBehavior[x]; x++){ forIn(tver, function(ruleSet, ruleSetName){ if(lang.isArray(ruleSet)){ darray.forEach(ruleSet, function(action){ _applyToNode(elem, action, ruleSetName); }); } }); } // ensure that re-application only adds new rules to the node elem[bid] = tBehavior.length; } ); }); }; }; dojo.behavior = new Behavior(); domReady( function(){ dojo.behavior.apply(); } ); return dojo.behavior; }); |
| 1 2 3 4 5 6 7 8 9 | 2 | define(["./_base/kernel", "./text"], function(dojo){ // module: // dojo/cache // dojo.cache is defined in dojo/text return dojo.cache; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | 2 | define(["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil){ // module: // dojo/colors /*===== return { // summary: // Color utilities, extending Base dojo.Color }; =====*/ var ColorExt = {}; lang.setObject("dojo.colors", ColorExt); //TODO: this module appears to break naming conventions // this is a standard conversion prescribed by the CSS3 Color Module var hue2rgb = function(m1, m2, h){ if(h < 0){ ++h; } if(h > 1){ --h; } var h6 = 6 * h; if(h6 < 1){ return m1 + (m2 - m1) * h6; } if(2 * h < 1){ return m2; } if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; } return m1; }; // Override base Color.fromRgb with the impl in this module dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo/_base/Color?*/ obj){ // summary: // get rgb(a) array from css-style color declarations // description: // this function can handle all 4 CSS3 Color Module formats: rgb, // rgba, hsl, hsla, including rgb(a) with percentage values. var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/); if(m){ var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a; if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){ var r = c[0]; if(r.charAt(r.length - 1) == "%"){ // 3 rgb percentage values a = ArrayUtil.map(c, function(x){ return parseFloat(x) * 2.56; }); if(l == 4){ a[3] = c[3]; } return Color.fromArray(a, obj); // dojo/_base/Color } return Color.fromArray(c, obj); // dojo/_base/Color } if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){ // normalize hsl values var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360, S = parseFloat(c[1]) / 100, L = parseFloat(c[2]) / 100, // calculate rgb according to the algorithm // recommended by the CSS3 Color Module m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, m1 = 2 * L - m2; a = [ hue2rgb(m1, m2, H + 1 / 3) * 256, hue2rgb(m1, m2, H) * 256, hue2rgb(m1, m2, H - 1 / 3) * 256, 1 ]; if(l == 4){ a[3] = c[3]; } return Color.fromArray(a, obj); // dojo/_base/Color } } return null; // dojo/_base/Color }; var confine = function(c, low, high){ // summary: // sanitize a color component by making sure it is a number, // and clamping it to valid values c = Number(c); return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number }; Color.prototype.sanitize = function(){ // summary: // makes sure that the object has correct attributes var t = this; t.r = Math.round(confine(t.r, 0, 255)); t.g = Math.round(confine(t.g, 0, 255)); t.b = Math.round(confine(t.b, 0, 255)); t.a = confine(t.a, 0, 1); return this; // dojo/_base/Color }; ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){ // summary: // creates a greyscale color with an optional alpha return Color.fromArray([g, g, g, a]); // dojo/_base/Color }; // mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings lang.mixin(Color.named, { "aliceblue": [240,248,255], "antiquewhite": [250,235,215], "aquamarine": [127,255,212], "azure": [240,255,255], "beige": [245,245,220], "bisque": [255,228,196], "blanchedalmond": [255,235,205], "blueviolet": [138,43,226], "brown": [165,42,42], "burlywood": [222,184,135], "cadetblue": [95,158,160], "chartreuse": [127,255,0], "chocolate": [210,105,30], "coral": [255,127,80], "cornflowerblue": [100,149,237], "cornsilk": [255,248,220], "crimson": [220,20,60], "cyan": [0,255,255], "darkblue": [0,0,139], "darkcyan": [0,139,139], "darkgoldenrod": [184,134,11], "darkgray": [169,169,169], "darkgreen": [0,100,0], "darkgrey": [169,169,169], "darkkhaki": [189,183,107], "darkmagenta": [139,0,139], "darkolivegreen": [85,107,47], "darkorange": [255,140,0], "darkorchid": [153,50,204], "darkred": [139,0,0], "darksalmon": [233,150,122], "darkseagreen": [143,188,143], "darkslateblue": [72,61,139], "darkslategray": [47,79,79], "darkslategrey": [47,79,79], "darkturquoise": [0,206,209], "darkviolet": [148,0,211], "deeppink": [255,20,147], "deepskyblue": [0,191,255], "dimgray": [105,105,105], "dimgrey": [105,105,105], "dodgerblue": [30,144,255], "firebrick": [178,34,34], "floralwhite": [255,250,240], "forestgreen": [34,139,34], "gainsboro": [220,220,220], "ghostwhite": [248,248,255], "gold": [255,215,0], "goldenrod": [218,165,32], "greenyellow": [173,255,47], "grey": [128,128,128], "honeydew": [240,255,240], "hotpink": [255,105,180], "indianred": [205,92,92], "indigo": [75,0,130], "ivory": [255,255,240], "khaki": [240,230,140], "lavender": [230,230,250], "lavenderblush": [255,240,245], "lawngreen": [124,252,0], "lemonchiffon": [255,250,205], "lightblue": [173,216,230], "lightcoral": [240,128,128], "lightcyan": [224,255,255], "lightgoldenrodyellow": [250,250,210], "lightgray": [211,211,211], "lightgreen": [144,238,144], "lightgrey": [211,211,211], "lightpink": [255,182,193], "lightsalmon": [255,160,122], "lightseagreen": [32,178,170], "lightskyblue": [135,206,250], "lightslategray": [119,136,153], "lightslategrey": [119,136,153], "lightsteelblue": [176,196,222], "lightyellow": [255,255,224], "limegreen": [50,205,50], "linen": [250,240,230], "magenta": [255,0,255], "mediumaquamarine": [102,205,170], "mediumblue": [0,0,205], "mediumorchid": [186,85,211], "mediumpurple": [147,112,219], "mediumseagreen": [60,179,113], "mediumslateblue": [123,104,238], "mediumspringgreen": [0,250,154], "mediumturquoise": [72,209,204], "mediumvioletred": [199,21,133], "midnightblue": [25,25,112], "mintcream": [245,255,250], "mistyrose": [255,228,225], "moccasin": [255,228,181], "navajowhite": [255,222,173], "oldlace": [253,245,230], "olivedrab": [107,142,35], "orange": [255,165,0], "orangered": [255,69,0], "orchid": [218,112,214], "palegoldenrod": [238,232,170], "palegreen": [152,251,152], "paleturquoise": [175,238,238], "palevioletred": [219,112,147], "papayawhip": [255,239,213], "peachpuff": [255,218,185], "peru": [205,133,63], "pink": [255,192,203], "plum": [221,160,221], "powderblue": [176,224,230], "rosybrown": [188,143,143], "royalblue": [65,105,225], "saddlebrown": [139,69,19], "salmon": [250,128,114], "sandybrown": [244,164,96], "seagreen": [46,139,87], "seashell": [255,245,238], "sienna": [160,82,45], "skyblue": [135,206,235], "slateblue": [106,90,205], "slategray": [112,128,144], "slategrey": [112,128,144], "snow": [255,250,250], "springgreen": [0,255,127], "steelblue": [70,130,180], "tan": [210,180,140], "thistle": [216,191,216], "tomato": [255,99,71], "turquoise": [64,224,208], "violet": [238,130,238], "wheat": [245,222,179], "whitesmoke": [245,245,245], "yellowgreen": [154,205,50] }); return Color; // TODO: return ColorExt, not Color }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | 2 | define(["./_base/kernel", "./regexp"], function(dojo, regexp){ // module: // dojo/cookie /*===== var __cookieProps = { // expires: Date|String|Number? // If a number, the number of days from today at which the cookie // will expire. If a date, the date past which the cookie will expire. // If expires is in the past, the cookie will be deleted. // If expires is omitted or is 0, the cookie will expire when the browser closes. // path: String? // The path to use for the cookie. // domain: String? // The domain to use for the cookie. // secure: Boolean? // Whether to only send the cookie on secure connections }; =====*/ dojo.cookie = function(/*String*/name, /*String?*/ value, /*__cookieProps?*/ props){ // summary: // Get or set a cookie. // description: // If one argument is passed, returns the value of the cookie // For two or more arguments, acts as a setter. // name: // Name of the cookie // value: // Value for the cookie // props: // Properties for the cookie // example: // set a cookie with the JSON-serialized contents of an object which // will expire 5 days from now: // | require(["dojo/cookie", "dojo/json"], function(cookie, json){ // | cookie("configObj", json.stringify(config, {expires: 5 })); // | }); // // example: // de-serialize a cookie back into a JavaScript object: // | require(["dojo/cookie", "dojo/json"], function(cookie, json){ // | config = json.parse(cookie("configObj")); // | }); // // example: // delete a cookie: // | require(["dojo/cookie"], function(cookie){ // | cookie("configObj", null, {expires: -1}); // | }); var c = document.cookie, ret; if(arguments.length == 1){ var matches = c.match(new RegExp("(?:^|; )" + regexp.escapeString(name) + "=([^;]*)")); ret = matches ? decodeURIComponent(matches[1]) : undefined; }else{ props = props || {}; // FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs? var exp = props.expires; if(typeof exp == "number"){ var d = new Date(); d.setTime(d.getTime() + exp*24*60*60*1000); exp = props.expires = d; } if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); } value = encodeURIComponent(value); var updatedCookie = name + "=" + value, propName; for(propName in props){ updatedCookie += "; " + propName; var propValue = props[propName]; if(propValue !== true){ updatedCookie += "=" + propValue; } } document.cookie = updatedCookie; } return ret; // String|undefined }; dojo.cookie.isSupported = function(){ // summary: // Use to determine if the current browser supports cookies or not. // // Returns true if user allows cookies. // Returns false if user doesn't allow cookies. if(!("cookieEnabled" in navigator)){ this("__djCookieTest__", "CookiesAllowed"); navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed"; if(navigator.cookieEnabled){ this("__djCookieTest__", "", {expires: -1}); } } return navigator.cookieEnabled; }; return dojo.cookie; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 2 | define([ "./_base/array", "./_base/lang", /*===== "./_base/declare", =====*/ "./number", "./i18n", "./i18n!./cldr/nls/currency", "./cldr/monetary" ], function(darray, lang, /*===== declare, =====*/ dnumber, i18n, nlsCurrency, cldrMonetary){ // module: // dojo/currency var currency = { // summary: // localized formatting and parsing routines for currencies // description: // extends dojo.number to provide culturally-appropriate formatting of values // in various world currencies, including use of a currency symbol. The currencies are specified // by a three-letter international symbol in all uppercase, and support for the currencies is // provided by the data in `dojo.cldr`. The scripts generating dojo.cldr specify which // currency support is included. A fixed number of decimal places is determined based // on the currency type and is not determined by the 'pattern' argument. The fractional // portion is optional, by default, and variable length decimals are not supported. }; lang.setObject("dojo.currency", currency); currency._mixInDefaults = function(options){ options = options || {}; options.type = "currency"; // Get locale-dependent currency data, like the symbol var bundle = i18n.getLocalization("dojo.cldr", "currency", options.locale) || {}; // Mixin locale-independent currency data, like # of places var iso = options.currency; var data = cldrMonetary.getData(iso); darray.forEach(["displayName","symbol","group","decimal"], function(prop){ data[prop] = bundle[iso+"_"+prop]; }); data.fractional = [true, false]; // Mixin with provided options return lang.mixin(data, options); }; /*===== currency.__FormatOptions = declare([dnumber.__FormatOptions], { // type: String? // Should not be set. Value is assumed to be "currency". // symbol: String? // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr` // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found. // currency: String? // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD". // For use with dojo.currency only. // places: Number? // number of decimal places to show. Default is defined based on which currency is used. type: "", symbol: "", currency: "", places: "" }); =====*/ currency.format = function(/*Number*/ value, /*__FormatOptions?*/ options){ // summary: // Format a Number as a currency, using locale-specific settings // // description: // Create a string from a Number using a known, localized pattern. // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Elements) // appropriate to the locale are chosen from the [CLDR](http://unicode.org/cldr) // as well as the appropriate symbols and delimiters and number of decimal places. // // value: // the number to be formatted. return dnumber.format(value, currency._mixInDefaults(options)); }; currency.regexp = function(/*dnumber.__RegexpOptions?*/ options){ // // summary: // Builds the regular needed to parse a currency value // // description: // Returns regular expression with positive and negative match, group and decimal separators // Note: the options.places default, the number of decimal places to accept, is defined by the currency type. return dnumber.regexp(currency._mixInDefaults(options)); // String }; /*===== var __ParseOptions = currency.__ParseOptions = declare(dnumber.__ParseOptions, { // type: String? // Should not be set. Value is assumed to be currency. // currency: String? // an [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code, a three letter sequence like "USD". // For use with dojo.currency only. // symbol: String? // localized currency symbol. The default will be looked up in table of supported currencies in `dojo.cldr` // A [ISO4217](http://en.wikipedia.org/wiki/ISO_4217) currency code will be used if not found. // places: Number? // fixed number of decimal places to accept. The default is determined based on which currency is used. // fractional: Boolean|Array? // Whether to include the fractional portion, where the number of decimal places are implied by the currency // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional. // By default for currencies, it the fractional portion is optional. }); =====*/ currency.parse = function(/*String*/ expression, /*__ParseOptions?*/ options){ // // summary: // Convert a properly formatted currency string to a primitive Number, // using locale-specific settings. // description: // Create a Number from a string using a known, localized pattern. // [Formatting patterns](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // are chosen appropriate to the locale, as well as the appropriate symbols and delimiters // and number of decimal places. // expression: // A string representation of a currency value return dnumber.parse(expression, currency._mixInDefaults(options)); }; return currency; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 | 2 | define(["./has", "./_base/lang"], function(has, lang){ // module: // dojo/date var date = { // summary: // Date manipulation utilities }; date.getDaysInMonth = function(/*Date*/dateObject){ // summary: // Returns the number of days in the month used by dateObject var month = dateObject.getMonth(); var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; if(month == 1 && date.isLeapYear(dateObject)){ return 29; } // Number return days[month]; // Number }; date.isLeapYear = function(/*Date*/dateObject){ // summary: // Determines if the year of the dateObject is a leap year // description: // Leap years are years with an additional day YYYY-02-29, where the // year number is a multiple of four with the following exception: If // a year is a multiple of 100, then it is only a leap year if it is // also a multiple of 400. For example, 1900 was not a leap year, but // 2000 is one. var year = dateObject.getFullYear(); return !(year%400) || (!(year%4) && !!(year%100)); // Boolean }; // FIXME: This is not localized date.getTimezoneName = function(/*Date*/dateObject){ // summary: // Get the user's time zone as provided by the browser // dateObject: // Needed because the timezone may vary with time (daylight savings) // description: // Try to get time zone info from toString or toLocaleString method of // the Date object -- UTC offset is not a time zone. See // http://www.twinsun.com/tz/tz-link.htm Note: results may be // inconsistent across browsers. var str = dateObject.toString(); // Start looking in toString var tz = ''; // The result -- return empty string if nothing found var match; // First look for something in parentheses -- fast lookup, no regex var pos = str.indexOf('('); if(pos > -1){ tz = str.substring(++pos, str.indexOf(')')); }else{ // If at first you don't succeed ... // If IE knows about the TZ, it appears before the year // Capital letters or slash before a 4-digit year // at the end of string var pat = /([A-Z\/]+) \d{4}$/; if((match = str.match(pat))){ tz = match[1]; }else{ // Some browsers (e.g. Safari) glue the TZ on the end // of toLocaleString instead of putting it in toString str = dateObject.toLocaleString(); // Capital letters or slash -- end of string, // after space pat = / ([A-Z\/]+)$/; if((match = str.match(pat))){ tz = match[1]; } } } // Make sure it doesn't somehow end up return AM or PM return (tz == 'AM' || tz == 'PM') ? '' : tz; // String }; // Utility methods to do arithmetic calculations with Dates date.compare = function(/*Date*/date1, /*Date?*/date2, /*String?*/portion){ // summary: // Compare two date objects by date, time, or both. // description: // Returns 0 if equal, positive if a > b, else negative. // date1: // Date object // date2: // Date object. If not specified, the current Date is used. // portion: // A string indicating the "date" or "time" portion of a Date object. // Compares both "date" and "time" by default. One of the following: // "date", "time", "datetime" // Extra step required in copy for IE - see #3112 date1 = new Date(+date1); date2 = new Date(+(date2 || new Date())); if(portion == "date"){ // Ignore times and compare dates. date1.setHours(0, 0, 0, 0); date2.setHours(0, 0, 0, 0); }else if(portion == "time"){ // Ignore dates and compare times. date1.setFullYear(0, 0, 0); date2.setFullYear(0, 0, 0); } if(date1 > date2){ return 1; } // int if(date1 < date2){ return -1; } // int return 0; // int }; date.add = function(/*Date*/date, /*String*/interval, /*int*/amount){ // summary: // Add to a Date in intervals of different size, from milliseconds to years // date: Date // Date object to start with // interval: // A string representing the interval. One of the following: // "year", "month", "day", "hour", "minute", "second", // "millisecond", "quarter", "week", "weekday" // amount: // How much to add to the date. var sum = new Date(+date); // convert to Number before copying to accomodate IE (#3112) var fixOvershoot = false; var property = "Date"; switch(interval){ case "day": break; case "weekday": //i18n FIXME: assumes Saturday/Sunday weekend, but this is not always true. see dojo/cldr/supplemental // Divide the increment time span into weekspans plus leftover days // e.g., 8 days is one 5-day weekspan / and two leftover days // Can't have zero leftover days, so numbers divisible by 5 get // a days value of 5, and the remaining days make up the number of weeks var days, weeks; var mod = amount % 5; if(!mod){ days = (amount > 0) ? 5 : -5; weeks = (amount > 0) ? ((amount-5)/5) : ((amount+5)/5); }else{ days = mod; weeks = parseInt(amount/5); } // Get weekday value for orig date param var strt = date.getDay(); // Orig date is Sat / positive incrementer // Jump over Sun var adj = 0; if(strt == 6 && amount > 0){ adj = 1; }else if(strt == 0 && amount < 0){ // Orig date is Sun / negative incrementer // Jump back over Sat adj = -1; } // Get weekday val for the new date var trgt = strt + days; // New date is on Sat or Sun if(trgt == 0 || trgt == 6){ adj = (amount > 0) ? 2 : -2; } // Increment by number of weeks plus leftover days plus // weekend adjustments amount = (7 * weeks) + days + adj; break; case "year": property = "FullYear"; // Keep increment/decrement from 2/29 out of March fixOvershoot = true; break; case "week": amount *= 7; break; case "quarter": // Naive quarter is just three months amount *= 3; // fallthrough... case "month": // Reset to last day of month if you overshoot fixOvershoot = true; property = "Month"; break; // case "hour": // case "minute": // case "second": // case "millisecond": default: property = "UTC"+interval.charAt(0).toUpperCase() + interval.substring(1) + "s"; } if(property){ sum["set"+property](sum["get"+property]()+amount); } if(fixOvershoot && (sum.getDate() < date.getDate())){ sum.setDate(0); } return sum; // Date }; date.difference = function(/*Date*/date1, /*Date?*/date2, /*String?*/interval){ // summary: // Get the difference in a specific unit of time (e.g., number of // months, weeks, days, etc.) between two dates, rounded to the // nearest integer. // date1: // Date object // date2: // Date object. If not specified, the current Date is used. // interval: // A string representing the interval. One of the following: // "year", "month", "day", "hour", "minute", "second", // "millisecond", "quarter", "week", "weekday" // // Defaults to "day". date2 = date2 || new Date(); interval = interval || "day"; var yearDiff = date2.getFullYear() - date1.getFullYear(); var delta = 1; // Integer return value switch(interval){ case "quarter": var m1 = date1.getMonth(); var m2 = date2.getMonth(); // Figure out which quarter the months are in var q1 = Math.floor(m1/3) + 1; var q2 = Math.floor(m2/3) + 1; // Add quarters for any year difference between the dates q2 += (yearDiff * 4); delta = q2 - q1; break; case "weekday": var days = Math.round(date.difference(date1, date2, "day")); var weeks = parseInt(date.difference(date1, date2, "week")); var mod = days % 7; // Even number of weeks if(mod == 0){ days = weeks*5; }else{ // Weeks plus spare change (< 7 days) var adj = 0; var aDay = date1.getDay(); var bDay = date2.getDay(); weeks = parseInt(days/7); mod = days % 7; // Mark the date advanced by the number of // round weeks (may be zero) var dtMark = new Date(date1); dtMark.setDate(dtMark.getDate()+(weeks*7)); var dayMark = dtMark.getDay(); // Spare change days -- 6 or less if(days > 0){ switch(true){ // Range starts on Sat case aDay == 6: adj = -1; break; // Range starts on Sun case aDay == 0: adj = 0; break; // Range ends on Sat case bDay == 6: adj = -1; break; // Range ends on Sun case bDay == 0: adj = -2; break; // Range contains weekend case (dayMark + mod) > 5: adj = -2; } }else if(days < 0){ switch(true){ // Range starts on Sat case aDay == 6: adj = 0; break; // Range starts on Sun case aDay == 0: adj = 1; break; // Range ends on Sat case bDay == 6: adj = 2; break; // Range ends on Sun case bDay == 0: adj = 1; break; // Range contains weekend case (dayMark + mod) < 0: adj = 2; } } days += adj; days -= (weeks*2); } delta = days; break; case "year": delta = yearDiff; break; case "month": delta = (date2.getMonth() - date1.getMonth()) + (yearDiff * 12); break; case "week": // Truncate instead of rounding // Don't use Math.floor -- value may be negative delta = parseInt(date.difference(date1, date2, "day")/7); break; case "day": delta /= 24; // fallthrough case "hour": delta /= 60; // fallthrough case "minute": delta /= 60; // fallthrough case "second": delta /= 1000; // fallthrough case "millisecond": delta *= date2.getTime() - date1.getTime(); } // Round for fractional values and DST leaps return Math.round(delta); // Number (integer) }; // Don't use setObject() because it may overwrite dojo/date/stamp (if that has already been loaded) has("extend-dojo") && lang.mixin(lang.getObject("dojo.date", true), date); return date; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 | 2 2 280 81 6 43 22 23 59 154 59 1 1 1 279 2 74 74 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 10 10 4 4 4 4 2 2 2 2 2 2 2 18 18 18 18 9 14 14 14 14 14 14 14 9 67 2 67 67 3 67 3 3 3 67 65 65 9 9 2 9 2 9 9 9 9 9 9 9 6 3 9 2 2 2 9 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 4 4 4 6 6 6 33 33 1 1 32 30 6 4 4 4 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 6 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 80 2 2 2 2 2 3 2 77 77 1 76 76 1 77 1 77 77 77 1 76 76 2 2 2 2 1 1 1 1 2 1 2 2 2 2 1 1 1 1 1 1 1 1 1 3 1 1 1 1 2 | (function(
userConfig,
defaultConfig
){
// summary:
// This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with
// any AMD-compliant loader via the package main module dojo/main.
// description:
// This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured
// to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded
// IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package
// via the package main module dojo/main and this loader is not required; see dojo/package.json for details.
//
// In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables
// the dojo.provide, dojo.require et al API. This machinery is loaded by default, but may be dynamically removed
// via the has.js API and statically removed via the build system.
//
// This loader includes sniffing machinery to determine the environment; the following environments are supported:
//
// - browser
// - node.js
// - rhino
//
// This is the so-called "source loader". As such, it includes many optional features that may be discarded by
// building a customized version with the build system.
// Design and Implementation Notes
//
// This is a dojo-specific adaption of bdLoad, donated to the dojo foundation by Altoviso LLC.
//
// This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition)
// loader that can be configured to operate in either synchronous or asynchronous modes.
//
// Since this machinery implements a loader, it does not have the luxury of using a load system and/or
// leveraging a utility library. This results in an unpleasantly long file; here is a road map of the contents:
//
// 1. Small library for use implementing the loader.
// 2. Define the has.js API; this is used throughout the loader to bracket features.
// 3. Define the node.js and rhino sniffs and sniff.
// 4. Define the loader's data.
// 5. Define the configuration machinery.
// 6. Define the script element sniffing machinery and sniff for configuration data.
// 7. Configure the loader IAW the provided user, default, and sniffing data.
// 8. Define the global require function.
// 9. Define the module resolution machinery.
// 10. Define the module and plugin module definition machinery
// 11. Define the script injection machinery.
// 12. Define the window load detection.
// 13. Define the logging API.
// 14. Define the tracing API.
// 16. Define the AMD define function.
// 17. Define the dojo v1.x provide/require machinery--so called "legacy" modes.
// 18. Publish global variables.
//
// Language and Acronyms and Idioms
//
// moduleId: a CJS module identifier, (used for public APIs)
// mid: moduleId (used internally)
// packageId: a package identifier (used for public APIs)
// pid: packageId (used internally); the implied system or default package has pid===""
// pack: package is used internally to reference a package object (since javascript has reserved words including "package")
// prid: plugin resource identifier
// The integer constant 1 is used in place of true and 0 in place of false.
// define a minimal library to help build the loader
var noop = function(){
},
isEmpty = function(it){
for(var p in it){
return 0;
}
return 1;
},
toString = {}.toString,
isFunction = function(it){
return toString.call(it) == "[object Function]";
},
isString = function(it){
return toString.call(it) == "[object String]";
},
isArray = function(it){
return toString.call(it) == "[object Array]";
},
forEach = function(vector, callback){
if(vector){
for(var i = 0; i < vector.length;){
callback(vector[i++]);
}
}
},
mix = function(dest, src){
for(var p in src){
dest[p] = src[p];
}
return dest;
},
makeError = function(error, info){
return mix(new Error(error), {src:"dojoLoader", info:info});
},
uidSeed = 1,
uid = function(){
// Returns a unique identifier (within the lifetime of the document) of the form /_d+/.
return "_" + uidSeed++;
},
// FIXME: how to doc window.require() api
// this will be the global require function; define it immediately so we can start hanging things off of it
req = function(
config, //(object, optional) hash of configuration properties
dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback
callback //(function, optional) lambda expression to apply to module values implied by dependencies
){
return contextRequire(config, dependencies, callback, 0, req);
},
// the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout
global = this,
doc = global.document,
element = doc && doc.createElement("DiV"),
has = req.has = function(name){
return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global, doc, element)) : hasCache[name];
},
hasCache = has.cache = defaultConfig.hasCache;
has.add = function(name, test, now, force){
(hasCache[name]===undefined || force) && (hasCache[name] = test);
return now && has(name);
};
has.add("host-node", userConfig.has && "host-node" in userConfig.has ?
userConfig.has["host-node"] :
(typeof process == "object" && process.versions && process.versions.node && process.versions.v8));
Eif(has("host-node")){
// fixup the default config for node.js environment
require("./_base/configNode.js").config(defaultConfig);
// remember node's require (with respect to baseUrl==dojo's root)
defaultConfig.loaderPatch.nodeRequire = require;
}
has.add("host-rhino", userConfig.has && "host-rhino" in userConfig.has ?
userConfig.has["host-rhino"] :
(typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object")));
Iif(has("host-rhino")){
// owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl...
for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){
arg = (rhinoArgs[i++] + "").split("=");
if(arg[0] == "baseUrl"){
baseUrl = arg[1];
break;
}
}
load(baseUrl + "/_base/configRhino.js");
rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs);
}
// userConfig has tests override defaultConfig has tests; do this after the environment detection because
// the environment detection usually sets some has feature values in the hasCache.
for(var p in userConfig.has){
has.add(p, userConfig.has[p], 0, 1);
}
//
// define the loader data
//
// the loader will use these like symbols if the loader has the traceApi; otherwise
// define magic numbers so that modules can be provided as part of defaultConfig
var requested = 1,
arrived = 2,
nonmodule = 3,
executing = 4,
executed = 5;
Eif(has("dojo-trace-api")){
// these make debugging nice; but using strings for symbols is a gross rookie error; don't do it for production code
requested = "requested";
arrived = "arrived";
nonmodule = "not-a-module";
executing = "executing";
executed = "executed";
}
var legacyMode = 0,
sync = "sync",
xd = "xd",
syncExecStack = [],
dojoRequirePlugin = 0,
checkDojoRequirePlugin = noop,
transformToAmd = noop,
getXhr;
Eif(has("dojo-sync-loader")){
req.isXdUrl = noop;
req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){
// the first dojo/_base/loader loaded gets to define these variables; they are designed to work
// in the presence of zero to many mapped dojo/_base/loaders
if(!dojoRequirePlugin){
dojoRequirePlugin = dojoRequirePlugin_;
checkDojoRequirePlugin = checkDojoRequirePlugin_;
transformToAmd = transformToAmd_;
}
return {
sync:sync,
requested:requested,
arrived:arrived,
nonmodule:nonmodule,
executing:executing,
executed:executed,
syncExecStack:syncExecStack,
modules:modules,
execQ:execQ,
getModule:getModule,
injectModule:injectModule,
setArrived:setArrived,
signal:signal,
finishExec:finishExec,
execModule:execModule,
dojoRequirePlugin:dojoRequirePlugin,
getLegacyMode:function(){return legacyMode;},
guardCheckComplete:guardCheckComplete
};
};
Iif(has("dom")){
// in legacy sync mode, the loader needs a minimal XHR library
var locationProtocol = location.protocol,
locationHost = location.host;
req.isXdUrl = function(url){
if(/^\./.test(url)){
// begins with a dot is always relative to page URL; therefore not xdomain
return false;
}
if(/^\/\//.test(url)){
// for v1.6- backcompat, url starting with // indicates xdomain
return true;
}
// get protocol and host
// \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file
// locationHost is falsy if file protocol => if locationProtocol matches and is "file:", || will return false
var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/);
return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost));
};
// note: to get the file:// protocol to work in FF, you must set security.fileuri.strict_origin_policy to false in about:config
has.add("dojo-xhr-factory", 1);
has.add("dojo-force-activex-xhr", has("host-browser") && !doc.addEventListener && window.location.protocol == "file:");
has.add("native-xhr", typeof XMLHttpRequest != "undefined");
if(has("native-xhr") && !has("dojo-force-activex-xhr")){
getXhr = function(){
return new XMLHttpRequest();
};
}else{
// if in the browser an old IE; find an xhr
for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){
try{
progid = XMLHTTP_PROGIDS[i++];
if(new ActiveXObject(progid)){
// this progid works; therefore, use it from now on
break;
}
}catch(e){
// squelch; we're just trying to find a good ActiveX progid
// if they all fail, then progid ends up as the last attempt and that will signal the error
// the first time the client actually tries to exec an xhr
}
}
getXhr = function(){
return new ActiveXObject(progid);
};
}
req.getXhr = getXhr;
has.add("dojo-gettext-api", 1);
req.getText = function(url, async, onLoad){
var xhr = getXhr();
xhr.open('GET', fixupUrl(url), false);
xhr.send(null);
if(xhr.status == 200 || (!location.host && !xhr.status)){
if(onLoad){
onLoad(xhr.responseText, async);
}
}else{
throw makeError("xhrFailed", xhr.status);
}
return xhr.responseText;
};
}
}else{
req.async = 1;
}
//
// loader eval
//
var eval_ =
// use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution
new Function('return eval(arguments[0]);');
req.eval =
function(text, hint){
return eval_(text + "\r\n////@ sourceURL=" + hint);
};
//
// loader micro events API
//
var listenerQueues = {},
error = "error",
signal = req.signal = function(type, args){
var queue = listenerQueues[type];
// notice we run a copy of the queue; this allows listeners to add/remove
// other listeners without affecting this particular signal
forEach(queue && queue.slice(0), function(listener){
listener.apply(null, isArray(args) ? args : [args]);
});
},
on = req.on = function(type, listener){
// notice a queue is not created until a client actually connects
var queue = listenerQueues[type] || (listenerQueues[type] = []);
queue.push(listener);
return {
remove:function(){
for(var i = 0; i<queue.length; i++){
if(queue[i]===listener){
queue.splice(i, 1);
return;
}
}
}
};
};
// configuration machinery; with an optimized/built defaultConfig, all configuration machinery can be discarded
// lexical variables hold key loader data structures to help with minification; these may be completely,
// one-time initialized by defaultConfig for optimized/built versions
var
aliases
// a vector of pairs of [regexs or string, replacement] => (alias, actual)
= [],
paths
// CommonJS paths
= {},
pathsMapProg
// list of (from-path, to-path, regex, length) derived from paths;
// a "program" to apply paths; see computeMapProg
= [],
packs
// a map from packageId to package configuration object; see fixupPackageInfo
= {},
map = req.map
// AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map
= {},
mapProgs
// vector of quads as described by computeMapProg; map-key is AMD map key, map-value is AMD map value
= [],
modules
// A hash:(mid) --> (module-object) the module namespace
//
// pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package
// mid: the fully-resolved (i.e., mappings have been applied) module identifier without the package identifier (e.g., "dojo/io/script")
// url: the URL from which the module was retrieved
// pack: the package object of the package to which the module belongs
// executed: 0 => not executed; executing => in the process of traversing deps and running factory; executed => factory has been executed
// deps: the dependency vector for this module (vector of modules objects)
// def: the factory for this module
// result: the result of the running the factory for this module
// injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define
// load: plugin load function; applicable only for plugins
//
// Modules go through several phases in creation:
//
// 1. Requested: some other module's definition or a require application contained the requested module in
// its dependency vector or executing code explicitly demands a module via req.require.
//
// 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL
//
// 3. Loaded: the resource injected in [2] has been evaluated.
//
// 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some
// resources may just contain a bundle of code and never formally define a module via define
//
// 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result.
= {},
cacheBust
// query string to append to module URLs to bust browser cache
= "",
cache
// hash:(mid | url)-->(function | string)
//
// A cache of resources. The resources arrive via a config.cache object, which is a hash from either mid --> function or
// url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided
// by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided
// by configl.cache always have a function value that causes the same code to execute as if the module was script injected.
//
// Both kinds of key-value pairs are entered into cache via the function consumePendingCache, which may relocate keys as given
// by any mappings *iff* the config.cache was received as part of a module resource request.
//
// Further, for mid keys, the implied url is computed and the value is entered into that key as well. This allows mapped modules
// to retrieve cached items that may have arrived consequent to another namespace.
//
= {},
urlKeyPrefix
// the prefix to prepend to a URL key in the cache.
= "url:",
pendingCacheInsert
// hash:(mid)-->(function)
//
// Gives a set of cache modules pending entry into cache. When cached modules are published to the loader, they are
// entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another
// independent set of cached modules. (1) is the usual case, and this case allows normalizing mids given in the pending
// cache for the local configuration, possibly relocating modules.
= {},
dojoSniffConfig
// map of configuration variables
// give the data-dojo-config as sniffed from the document (if any)
= {},
insertPointSibling
// the nodes used to locate where scripts are injected into the document
= 0;
Eif(has("dojo-config-api")){
var consumePendingCacheInsert = function(referenceModule){
var p, item, match, now, m;
for(p in pendingCacheInsert){
item = pendingCacheInsert[p];
match = p.match(/^url\:(.+)/);
if(match){
cache[urlKeyPrefix + toUrl(match[1], referenceModule)] = item;
}else if(p=="*now"){
now = item;
}else if(p!="*noref"){
m = getModuleInfo(p, referenceModule, true);
cache[m.mid] = cache[urlKeyPrefix + m.url] = item;
}
}
Iif(now){
now(createRequire(referenceModule));
}
pendingCacheInsert = {};
},
escapeString = function(s){
return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){ return "\\" + c; });
},
computeMapProg = function(map, dest){
// This routine takes a map as represented by a JavaScript object and initializes dest, a vector of
// quads of (map-key, map-value, refex-for-map-key, length-of-map-key), sorted decreasing by length-
// of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning
// of a the search source. Notice the map-value is irrelevant to the algorithm
dest.splice(0, dest.length);
for(var p in map){
dest.push([
p,
map[p],
new RegExp("^" + escapeString(p) + "(\/|$)"),
p.length]);
}
dest.sort(function(lhs, rhs){ return rhs[3] - lhs[3]; });
return dest;
},
computeAliases = function(config, dest){
forEach(config, function(pair){
// take a fixed-up copy...
dest.push([isString(pair[0]) ? new RegExp("^" + escapeString(pair[0]) + "$") : pair[0], pair[1]]);
});
},
fixupPackageInfo = function(packageInfo){
// calculate the precise (name, location, main, mappings) for a package
var name = packageInfo.name;
Iif(!name){
// packageInfo must be a string that gives the name
name = packageInfo;
packageInfo = {name:name};
}
packageInfo = mix({main:"main"}, packageInfo);
packageInfo.location = packageInfo.location ? packageInfo.location : name;
// packageMap is deprecated in favor of AMD map
Iif(packageInfo.packageMap){
map[name] = packageInfo.packageMap;
}
Iif(!packageInfo.main.indexOf("./")){
packageInfo.main = packageInfo.main.substring(2);
}
// now that we've got a fully-resolved package object, push it into the configuration
packs[name] = packageInfo;
},
delayedModuleConfig
// module config cannot be consumed until the loader is completely initialized; therefore, all
// module config detected during booting is memorized and applied at the end of loader initialization
// TODO: this is a bit of a kludge; all config should be moved to end of loader initialization, but
// we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete
= [],
config = function(config, booting, referenceModule){
for(var p in config){
if(p=="waitSeconds"){
req.waitms = (config[p] || 0) * 1000;
}
Iif(p=="cacheBust"){
cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : "";
}
if(p=="baseUrl" || p=="combo"){
req[p] = config[p];
}
if(has("dojo-sync-loader") && p=="async"){
// falsy or "sync" => legacy sync loader
// "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable, set automatically by the loader)
// "legacyAsync" => permanently in "xd" by choice
// "debugAtAllCosts" => trying to load everything via script injection (not implemented)
// otherwise, must be truthy => AMD
// legacyMode: sync | legacyAsync | xd | false
var mode = config[p];
req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false));
req.async = !legacyMode;
}
if(config[p]!==hasCache){
// accumulate raw config info for client apps which can use this to pass their own config
req.rawConfig[p] = config[p];
p!="has" && has.add("config-"+p, config[p], 0, booting);
}
}
// make sure baseUrl exists
Iif(!req.baseUrl){
req.baseUrl = "./";
}
// make sure baseUrl ends with a slash
if(!/\/$/.test(req.baseUrl)){
req.baseUrl += "/";
}
// now do the special work for has, packages, packagePaths, paths, aliases, and cache
for(p in config.has){
has.add(p, config.has[p], 0, booting);
}
// for each package found in any packages config item, augment the packs map owned by the loader
forEach(config.packages, fixupPackageInfo);
// for each packagePath found in any packagePaths config item, augment the packageConfig
// packagePaths is deprecated; remove in 2.0
for(baseUrl in config.packagePaths){
forEach(config.packagePaths[baseUrl], function(packageInfo){
var location = baseUrl + "/" + packageInfo;
if(isString(packageInfo)){
packageInfo = {name:packageInfo};
}
packageInfo.location = location;
fixupPackageInfo(packageInfo);
});
}
// notice that computeMapProg treats the dest as a reference; therefore, if/when that variable
// is published (see dojo-publish-privates), the published variable will always hold a valid value.
// this must come after all package processing since package processing may mutate map
computeMapProg(mix(map, config.map), mapProgs);
forEach(mapProgs, function(item){
item[1] = computeMapProg(item[1], []);
if(item[0]=="*"){
mapProgs.star = item;
}
});
// push in any paths and recompute the internal pathmap
computeMapProg(mix(paths, config.paths), pathsMapProg);
// aliases
computeAliases(config.aliases, aliases);
if(booting){
delayedModuleConfig.push({config:config.config});
}else{
for(p in config.config){
var module = getModule(p, referenceModule);
module.config = mix(module.config || {}, config.config[p]);
}
}
// push in any new cache values
if(config.cache){
consumePendingCacheInsert();
pendingCacheInsert = config.cache;
Iif(config.cache["*noref"]){
consumePendingCacheInsert();
}
}
signal("config", [config, req.rawConfig]);
};
//
// execute the various sniffs; userConfig can override and value
//
Iif(has("dojo-cdn") || has("dojo-sniff")){
// the sniff regex looks for a src attribute ending in dojo.js, optionally preceded with a path.
// match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the
// dojo location on CDN deployments and baseUrl when either/both of these are not provided
// explicitly in the config data; this is the 1.6- behavior.
var scripts = doc.getElementsByTagName("script"),
i = 0,
script, dojoDir, src, match;
while(i < scripts.length){
script = scripts[i++];
if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){
// sniff dojoDir and baseUrl
dojoDir = match[3] || "";
defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir;
// remember an insertPointSibling
insertPointSibling = script;
}
// sniff configuration on attribute in script element
if((src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")))){
dojoSniffConfig = req.eval("({ " + src + " })", "data-dojo-config");
// remember an insertPointSibling
insertPointSibling = script;
}
// sniff requirejs attribute
if(has("dojo-requirejs-api")){
if((src = script.getAttribute("data-main"))){
dojoSniffConfig.deps = dojoSniffConfig.deps || [src];
}
}
}
}
Iif(has("dojo-test-sniff")){
// pass down doh.testConfig from parent as if it were a data-dojo-config
try{
if(window.parent != window && window.parent.require){
var doh = window.parent.require("doh");
doh && mix(dojoSniffConfig, doh.testConfig);
}
}catch(e){}
}
// configure the loader; let the user override defaults
req.rawConfig = {};
config(defaultConfig, 1);
// do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides
Iif(has("dojo-cdn")){
packs.dojo.location = dojoDir;
if(dojoDir){
dojoDir += "/";
}
packs.dijit.location = dojoDir + "../dijit/";
packs.dojox.location = dojoDir + "../dojox/";
}
config(userConfig, 1);
config(dojoSniffConfig, 1);
}else{
// no config API, assume defaultConfig has everything the loader needs...for the entire lifetime of the application
paths = defaultConfig.paths;
pathsMapProg = defaultConfig.pathsMapProg;
packs = defaultConfig.packs;
aliases = defaultConfig.aliases;
mapProgs = defaultConfig.mapProgs;
modules = defaultConfig.modules;
cache = defaultConfig.cache;
cacheBust = defaultConfig.cacheBust;
// remember the default config for other processes (e.g., dojo/config)
req.rawConfig = defaultConfig;
}
Iif(has("dojo-combo-api")){
req.combo = req.combo || {add:noop};
var comboPending = 0,
combosPending = [],
comboPendingTimer = null;
}
// build the loader machinery iaw configuration, including has feature tests
var injectDependencies = function(module){
// checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies
guardCheckComplete(function(){
forEach(module.deps, injectModule);
if(has("dojo-combo-api") && comboPending && !comboPendingTimer){
comboPendingTimer = setTimeout(function() {
comboPending = 0;
comboPendingTimer = null;
req.combo.done(function(mids, url) {
var onLoadCallback= function(){
// defQ is a vector of module definitions 1-to-1, onto mids
runDefQ(0, mids);
checkComplete();
};
combosPending.push(mids);
injectingModule = mids;
req.injectUrl(url, onLoadCallback, mids);
injectingModule = 0;
}, req);
}, 0);
}
});
},
contextRequire = function(a1, a2, a3, referenceModule, contextRequire){
var module, syntheticMid;
Iif(isString(a1)){
// signature is (moduleId)
module = getModule(a1, referenceModule, true);
if(module && module.executed){
return module.result;
}
throw makeError("undefinedModule", a1);
}
Iif(!isArray(a1)){
// a1 is a configuration
config(a1, 0, referenceModule);
// juggle args; (a2, a3) may be (dependencies, callback)
a1 = a2;
a2 = a3;
}
Eif(isArray(a1)){
// signature is (requestList [,callback])
Iif(!a1.length){
a2 && a2();
}else{
syntheticMid = "require*" + uid();
// resolve the request list with respect to the reference module
for(var mid, deps = [], i = 0; i < a1.length;){
mid = a1[i++];
deps.push(getModule(mid, referenceModule));
}
// construct a synthetic module to control execution of the requestList, and, optionally, callback
module = mix(makeModuleInfo("", syntheticMid, 0, ""), {
injected: arrived,
deps: deps,
def: a2 || noop,
require: referenceModule ? referenceModule.require : req,
gc: 1 //garbage collect
});
modules[module.mid] = module;
// checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies
injectDependencies(module);
// try to immediately execute
// if already traversing a factory tree, then strict causes circular dependency to abort the execution; maybe
// it's possible to execute this require later after the current traversal completes and avoid the circular dependency.
// ...but *always* insist on immediate in synch mode
var strict = checkCompleteGuard && legacyMode!=sync;
guardCheckComplete(function(){
execModule(module, strict);
});
if(!module.executed){
// some deps weren't on board or circular dependency detected and strict; therefore, push into the execQ
execQ.push(module);
}
checkComplete();
}
}
return contextRequire;
},
createRequire = function(module){
if(!module){
return req;
}
var result = module.require;
if(!result){
result = function(a1, a2, a3){
return contextRequire(a1, a2, a3, module, result);
};
module.require = mix(result, req);
result.module = module;
result.toUrl = function(name){
return toUrl(name, module);
};
result.toAbsMid = function(mid){
return toAbsMid(mid, module);
};
if(has("dojo-undef-api")){
result.undef = function(mid){
req.undef(mid, module);
};
}
if(has("dojo-sync-loader")){
result.syncLoadNls = function(mid){
var nlsModuleInfo = getModuleInfo(mid, module),
nlsModule = modules[nlsModuleInfo.mid];
if(!nlsModule || !nlsModule.executed){
cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url];
if(cached){
evalModuleText(cached);
nlsModule = modules[nlsModuleInfo.mid];
}
}
return nlsModule && nlsModule.executed && nlsModule.result;
};
}
}
return result;
},
execQ =
// The list of modules that need to be evaluated.
[],
defQ =
// The queue of define arguments sent to loader.
[],
waiting =
// The set of modules upon which the loader is waiting for definition to arrive
{},
setRequested = function(module){
module.injected = requested;
waiting[module.mid] = 1;
Eif(module.url){
waiting[module.url] = module.pack || 1;
}
startTimer();
},
setArrived = function(module){
module.injected = arrived;
delete waiting[module.mid];
if(module.url){
delete waiting[module.url];
}
if(isEmpty(waiting)){
clearTimer();
has("dojo-sync-loader") && legacyMode==xd && (legacyMode = sync);
}
},
execComplete = req.idle =
// says the loader has completed (or not) its work
function(){
return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard;
},
runMapProg = function(targetMid, map){
// search for targetMid in map; return the map item if found; falsy otherwise
Eif(map){
for(var i = 0; i < map.length; i++){
if(map[i][2].test(targetMid)){
return map[i];
}
}
}
return 0;
},
compactPath = function(path){
var result = [],
segment, lastSegment;
path = path.replace(/\\/g, '/').split('/');
while(path.length){
segment = path.shift();
if(segment==".." && result.length && lastSegment!=".."){
result.pop();
lastSegment = result[result.length - 1];
}else if(segment!="."){
result.push(lastSegment= segment);
} // else ignore "."
}
return result.join("/");
},
makeModuleInfo = function(pid, mid, pack, url){
Eif(has("dojo-sync-loader")){
var xd= req.isXdUrl(url);
return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0, isXd:xd, isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))};
}else{
return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0};
}
},
getModuleInfo_ = function(mid, referenceModule, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate){
// arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder)
// alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader
var pid, pack, midInPackage, mapItem, url, result, isRelative, requestedMid;
requestedMid = mid;
isRelative = /^\./.test(mid);
Iif(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){
// absolute path or protocol of .js filetype, or relative path but no reference module and therefore relative to page
// whatever it is, it's not a module but just a URL of some sort
// note: pid===0 indicates the routine is returning an unmodified mid
return makeModuleInfo(0, mid, 0, mid);
}else{
// relative module ids are relative to the referenceModule; get rid of any dots
mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid);
Iif(/^\./.test(mid)){
throw makeError("irrationalPath", mid);
}
// at this point, mid is an absolute mid
// map the mid
if(referenceModule){
mapItem = runMapProg(referenceModule.mid, mapProgs);
}
mapItem = mapItem || mapProgs.star;
mapItem = mapItem && runMapProg(mid, mapItem[1]);
Iif(mapItem){
mid = mapItem[1] + mid.substring(mapItem[3]);
}
match = mid.match(/^([^\/]+)(\/(.+))?$/);
pid = match ? match[1] : "";
Eif((pack = packs[pid])){
mid = pid + "/" + (midInPackage = (match[3] || pack.main));
}else{
pid = "";
}
// search aliases
var candidateLength = 0,
candidate = 0;
forEach(aliases, function(pair){
var match = mid.match(pair[0]);
if(match && match.length>candidateLength){
candidate = isFunction(pair[1]) ? mid.replace(pair[0], pair[1]) : pair[1];
}
});
Iif(candidate){
return getModuleInfo_(candidate, 0, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate);
}
result = modules[mid];
Iif(result){
return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pack, result.url) : modules[mid];
}
}
// get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the
// fully resolved (i.e., all relative indicators and package mapping resolved) module id
// note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid
mapItem = runMapProg(mid, pathsMapProg);
Iif(mapItem){
url = mapItem[1] + mid.substring(mapItem[3]);
}else Eif(pid){
url = pack.location + "/" + midInPackage;
}else if(has("config-tlmSiblingOfDojo")){
url = "../" + mid;
}else{
url = mid;
}
// if result is not absolute, add baseUrl
Eif(!(/(^\/)|(\:)/.test(url))){
url = baseUrl + url;
}
url += ".js";
return makeModuleInfo(pid, mid, pack, compactPath(url));
},
getModuleInfo = function(mid, referenceModule, fromPendingCache){
return getModuleInfo_(mid, referenceModule, packs, modules, req.baseUrl, fromPendingCache ? [] : mapProgs, fromPendingCache ? [] : pathsMapProg, fromPendingCache ? [] : aliases);
},
resolvePluginResourceId = function(plugin, prid, referenceModule){
return plugin.normalize ? plugin.normalize(prid, function(mid){return toAbsMid(mid, referenceModule);}) : toAbsMid(prid, referenceModule);
},
dynamicPluginUidGenerator = 0,
getModule = function(mid, referenceModule, immediate){
// compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule
var match, plugin, prid, result;
match = mid.match(/^(.+?)\!(.*)$/);
Iif(match){
// name was <plugin-module>!<plugin-resource-id>
plugin = getModule(match[1], referenceModule, immediate);
if(has("dojo-sync-loader") && legacyMode == sync && !plugin.executed){
injectModule(plugin);
if(plugin.injected===arrived && !plugin.executed){
guardCheckComplete(function(){
execModule(plugin);
});
}
if(plugin.executed){
promoteModuleToPlugin(plugin);
}else{
// we are in xdomain mode for some reason
execQ.unshift(plugin);
}
}
if(plugin.executed === executed && !plugin.load){
// executed the module not knowing it was a plugin
promoteModuleToPlugin(plugin);
}
// if the plugin has not been loaded, then can't resolve the prid and must assume this plugin is dynamic until we find out otherwise
if(plugin.load){
prid = resolvePluginResourceId(plugin, match[2], referenceModule);
mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid);
}else{
prid = match[2];
mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin";
}
result = {plugin:plugin, mid:mid, req:createRequire(referenceModule), prid:prid};
}else{
result = getModuleInfo(mid, referenceModule);
}
return modules[result.mid] || (!immediate && (modules[result.mid] = result));
},
toAbsMid = req.toAbsMid = function(mid, referenceModule){
return getModuleInfo(mid, referenceModule).mid;
},
toUrl = req.toUrl = function(name, referenceModule){
var moduleInfo = getModuleInfo(name+"/x", referenceModule),
url= moduleInfo.url;
return fixupUrl(moduleInfo.pid===0 ?
// if pid===0, then name had a protocol or absolute path; either way, toUrl is the identify function in such cases
name :
// "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look like a module id
url.substring(0, url.length-5)
);
},
nonModuleProps = {
injected: arrived,
executed: executed,
def: nonmodule,
result: nonmodule
},
makeCjs = function(mid){
return modules[mid] = mix({mid:mid}, nonModuleProps);
},
cjsRequireModule = makeCjs("require"),
cjsExportsModule = makeCjs("exports"),
cjsModuleModule = makeCjs("module"),
runFactory = function(module, args){
req.trace("loader-run-factory", [module.mid]);
var factory = module.def,
result;
has("dojo-sync-loader") && syncExecStack.unshift(module);
if(has("config-dojo-loader-catches")){
try{
result= isFunction(factory) ? factory.apply(null, args) : factory;
}catch(e){
signal(error, module.result = makeError("factoryThrew", [module, e]));
}
}else{
result= isFunction(factory) ? factory.apply(null, args) : factory;
}
module.result = result===undefined && module.cjs ? module.cjs.exports : result;
has("dojo-sync-loader") && syncExecStack.shift(module);
},
abortExec = {},
defOrder = 0,
promoteModuleToPlugin = function(pluginModule){
var plugin = pluginModule.result;
pluginModule.dynamic = plugin.dynamic;
pluginModule.normalize = plugin.normalize;
pluginModule.load = plugin.load;
return pluginModule;
},
resolvePluginLoadQ = function(plugin){
// plugins is a newly executed module that has a loadQ waiting to run
// step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid
// recall the original mid was created before the plugin was on board and therefore it was impossible to
// compute the final mid; accordingly, prid may or may not change, but the mid will definitely change
var map = {};
forEach(plugin.loadQ, function(pseudoPluginResource){
// manufacture and insert the real module in modules
var prid = resolvePluginResourceId(plugin, pseudoPluginResource.prid, pseudoPluginResource.req.module),
mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/, prid) : (plugin.mid + "!" + prid),
pluginResource = mix(mix({}, pseudoPluginResource), {mid:mid, prid:prid, injected:0});
if(!modules[mid]){
// create a new (the real) plugin resource and inject it normally now that the plugin is on board
injectPlugin(modules[mid] = pluginResource);
} // else this was a duplicate request for the same (plugin, rid) for a nondynamic plugin
// pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board)
// mark is as arrived and delete it from modules; the real module was requested above
map[pseudoPluginResource.mid] = modules[mid];
setArrived(pseudoPluginResource);
delete modules[pseudoPluginResource.mid];
});
plugin.loadQ = 0;
// step2: replace all references to any placeholder modules with real modules
var substituteModules = function(module){
for(var replacement, deps = module.deps || [], i = 0; i<deps.length; i++){
replacement = map[deps[i].mid];
if(replacement){
deps[i] = replacement;
}
}
};
for(var p in modules){
substituteModules(modules[p]);
}
forEach(execQ, substituteModules);
},
finishExec = function(module){
req.trace("loader-finish-exec", [module.mid]);
module.executed = executed;
module.defOrder = defOrder++;
has("dojo-sync-loader") && forEach(module.provides, function(cb){ cb(); });
if(module.loadQ){
// the module was a plugin
promoteModuleToPlugin(module);
resolvePluginLoadQ(module);
}
// remove all occurrences of this module from the execQ
for(i = 0; i < execQ.length;){
if(execQ[i] === module){
execQ.splice(i, 1);
}else{
i++;
}
}
// delete references to synthetic modules
if (/^require\*/.test(module.mid)) {
delete modules[module.mid];
}
},
circleTrace = [],
execModule = function(module, strict){
// run the dependency vector, then run the factory for module
if(module.executed === executing){
req.trace("loader-circular-dependency", [circleTrace.concat(module.mid).join("->")]);
return (!module.def || strict) ? abortExec : (module.cjs && module.cjs.exports);
}
// at this point the module is either not executed or fully executed
if(!module.executed){
if(!module.def){
return abortExec;
}
var mid = module.mid,
deps = module.deps || [],
arg, argResult,
args = [],
i = 0;
if(has("dojo-trace-api")){
circleTrace.push(mid);
req.trace("loader-exec-module", ["exec", circleTrace.length, mid]);
}
// for circular dependencies, assume the first module encountered was executed OK
// modules that circularly depend on a module that has not run its factory will get
// the pre-made cjs.exports===module.result. They can take a reference to this object and/or
// add properties to it. When the module finally runs its factory, the factory can
// read/write/replace this object. Notice that so long as the object isn't replaced, any
// reference taken earlier while walking the deps list is still valid.
module.executed = executing;
while((arg = deps[i++])){
argResult = ((arg === cjsRequireModule) ? createRequire(module) :
((arg === cjsExportsModule) ? module.cjs.exports :
((arg === cjsModuleModule) ? module.cjs :
execModule(arg, strict))));
if(argResult === abortExec){
module.executed = 0;
req.trace("loader-exec-module", ["abort", mid]);
has("dojo-trace-api") && circleTrace.pop();
return abortExec;
}
args.push(argResult);
}
runFactory(module, args);
finishExec(module);
has("dojo-trace-api") && circleTrace.pop();
}
// at this point the module is guaranteed fully executed
return module.result;
},
checkCompleteGuard = 0,
guardCheckComplete = function(proc){
try{
checkCompleteGuard++;
proc();
}finally{
checkCompleteGuard--;
}
if(execComplete()){
signal("idle", []);
}
},
checkComplete = function(){
// keep going through the execQ as long as at least one factory is executed
// plugins, recursion, cached modules all make for many execution path possibilities
if(checkCompleteGuard){
return;
}
guardCheckComplete(function(){
checkDojoRequirePlugin();
for(var currentDefOrder, module, i = 0; i < execQ.length;){
currentDefOrder = defOrder;
module = execQ[i];
execModule(module);
if(currentDefOrder!=defOrder){
// defOrder was bumped one or more times indicating something was executed (note, this indicates
// the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute)
checkDojoRequirePlugin();
i = 0;
}else{
// nothing happened; check the next module in the exec queue
i++;
}
}
});
};
Iif(has("dojo-undef-api")){
req.undef = function(moduleId, referenceModule){
// In order to reload a module, it must be undefined (this routine) and then re-requested.
// This is useful for testing frameworks (at least).
var module = getModule(moduleId, referenceModule);
setArrived(module);
mix(module, {def:0, executed:0, injected:0, node:0});
};
}
Eif(has("dojo-inject-api")){
Eif(has("dojo-loader-eval-hint-url")===undefined){
has.add("dojo-loader-eval-hint-url", 1);
}
var fixupUrl= function(url){
url += ""; // make sure url is a Javascript string (some paths may be a Java string)
return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : "");
},
injectPlugin = function(
module
){
// injects the plugin module given by module; may have to inject the plugin itself
var plugin = module.plugin;
if(plugin.executed === executed && !plugin.load){
// executed the module not knowing it was a plugin
promoteModuleToPlugin(plugin);
}
var onLoad = function(def){
module.result = def;
setArrived(module);
finishExec(module);
checkComplete();
};
if(plugin.load){
plugin.load(module.prid, module.req, onLoad);
}else if(plugin.loadQ){
plugin.loadQ.push(module);
}else{
// the unshift instead of push is important: we don't want plugins to execute as
// dependencies of some other module because this may cause circles when the plugin
// loadQ is run; also, generally, we want plugins to run early since they may load
// several other modules and therefore can potentially unblock many modules
plugin.loadQ = [module];
execQ.unshift(plugin);
injectModule(plugin);
}
},
// for IE, injecting a module may result in a recursive execution if the module is in the cache
cached = 0,
injectingModule = 0,
injectingCachedModule = 0,
evalModuleText = function(text, module){
// see def() for the injectingCachedModule bracket; it simply causes a short, safe circuit
if(has("config-stripStrict")){
text = text.replace(/"use strict"/g, '');
}
injectingCachedModule = 1;
if(has("config-dojo-loader-catches")){
try{
if(text===cached){
cached.call(null);
}else{
req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid);
}
}catch(e){
signal(error, makeError("evalModuleThrew", module));
}
}else{
if(text===cached){
cached.call(null);
}else{
req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid);
}
}
injectingCachedModule = 0;
},
injectModule = function(module){
// Inject the module. In the browser environment, this means appending a script element into
// the document; in other environments, it means loading a file.
//
// If in synchronous mode, then get the module synchronously if it's not xdomainLoading.
var mid = module.mid,
url = module.url;
Iif(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){
return;
}
setRequested(module);
Iif(has("dojo-combo-api")){
var viaCombo = 0;
if(module.plugin && module.plugin.isCombo){
// a combo plugin; therefore, must be handled by combo service
// the prid should have already been converted to a URL (if required by the plugin) during
// the normalize process; in any event, there is no way for the loader to know how to
// to the conversion; therefore the third argument is zero
req.combo.add(module.plugin.mid, module.prid, 0, req);
viaCombo = 1;
}else if(!module.plugin){
viaCombo = req.combo.add(0, module.mid, module.url, req);
}
if(viaCombo){
comboPending= 1;
return;
}
}
Iif(module.plugin){
injectPlugin(module);
return;
} // else a normal module (not a plugin)
var onLoadCallback = function(){
runDefQ(module);
if(module.injected !== arrived){
// the script that contained the module arrived and has been executed yet
// nothing was added to the defQ (so it wasn't an AMD module) and the module
// wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module);
// therefore, it must not have been a module; adjust state accordingly
if(has("dojo-enforceDefine")){
signal(error, makeError("noDefine", module));
return;
}
setArrived(module);
mix(module, nonModuleProps);
req.trace("loader-define-nonmodule", [module.url]);
}
if(has("dojo-sync-loader") && legacyMode){
// must call checkComplete even in for sync loader because we may be in xdomainLoading mode;
// but, if xd loading, then don't call checkComplete until out of the current sync traversal
// in order to preserve order of execution of the dojo.required modules
!syncExecStack.length && checkComplete();
}else{
checkComplete();
}
};
cached = cache[mid] || cache[urlKeyPrefix + module.url];
Iif(cached){
req.trace("loader-inject", ["cache", module.mid, url]);
evalModuleText(cached, module);
onLoadCallback();
return;
}
Eif(has("dojo-sync-loader") && legacyMode){
Iif(module.isXd){
// switch to async mode temporarily; if current legacyMode!=sync, then is must be one of {legacyAsync, xd, false}
legacyMode==sync && (legacyMode = xd);
// fall through and load via script injection
}else Iif(module.isAmd && legacyMode!=sync){
// fall through and load via script injection
}else{
// mode may be sync, xd/legacyAsync, or async; module may be AMD or legacy; but module is always located on the same domain
var xhrCallback = function(text){
if(legacyMode==sync){
// the top of syncExecStack gives the current synchronously executing module; the loader needs
// to know this if it has to switch to async loading in the middle of evaluating a legacy module
// this happens when a modules dojo.require's a module that must be loaded async because it's xdomain
// (using unshift/shift because there is no back() methods for Javascript arrays)
syncExecStack.unshift(module);
evalModuleText(text, module);
syncExecStack.shift();
// maybe the module was an AMD module
runDefQ(module);
// legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing
if(!module.cjs){
setArrived(module);
finishExec(module);
}
if(module.finish){
// while synchronously evaluating this module, dojo.require was applied referencing a module
// that had to be loaded async; therefore, the loader stopped answering all dojo.require
// requests so they could be answered completely in the correct sequence; module.finish gives
// the list of dojo.requires that must be re-applied once all target modules are available;
// make a synthetic module to execute the dojo.require's in the correct order
// compute a guaranteed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module
// TODO: can we just leave the module.finish...what's it hurting?
var finishMid = mid + "*finish",
finish = module.finish;
delete module.finish;
def(finishMid, ["dojo", ("dojo/require!" + finish.join(",")).replace(/\./g, "/")], function(dojo){
forEach(finish, function(mid){ dojo.require(mid); });
});
// unshift, not push, which causes the current traversal to be reattempted from the top
execQ.unshift(getModule(finishMid));
}
onLoadCallback();
}else{
text = transformToAmd(module, text);
if(text){
evalModuleText(text, module);
onLoadCallback();
}else{
// if transformToAmd returned falsy, then the module was already AMD and it can be script-injected
// do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache)
injectingModule = module;
req.injectUrl(fixupUrl(url), onLoadCallback, module);
injectingModule = 0;
}
}
};
req.trace("loader-inject", ["xhr", module.mid, url, legacyMode!=sync]);
Iif(has("config-dojo-loader-catches")){
try{
req.getText(url, legacyMode!=sync, xhrCallback);
}catch(e){
signal(error, makeError("xhrInjectFailed", [module, e]));
}
}else{
req.getText(url, legacyMode!=sync, xhrCallback);
}
return;
}
} // else async mode or fell through in xdomain loading mode; either way, load by script injection
req.trace("loader-inject", ["script", module.mid, url]);
injectingModule = module;
req.injectUrl(fixupUrl(url), onLoadCallback, module);
injectingModule = 0;
},
defineModule = function(module, deps, def){
req.trace("loader-define-module", [module.mid, deps]);
Iif(has("dojo-combo-api") && module.plugin && module.plugin.isCombo){
// the module is a plugin resource loaded by the combo service
// note: check for module.plugin should be enough since normal plugin resources should
// not follow this path; module.plugin.isCombo is future-proofing belt and suspenders
module.result = isFunction(def) ? def() : def;
setArrived(module);
finishExec(module);
return module;
}
var mid = module.mid;
Iif(module.injected === arrived){
signal(error, makeError("multipleDefine", module));
return module;
}
mix(module, {
deps: deps,
def: def,
cjs: {
id: module.mid,
uri: module.url,
exports: (module.result = {}),
setExports: function(exports){
module.cjs.exports = exports;
},
config:function(){
return module.config;
}
}
});
// resolve deps with respect to this module
for(var i = 0; deps[i]; i++){
deps[i] = getModule(deps[i], module);
}
Eif(has("dojo-sync-loader") && legacyMode && !waiting[mid]){
// the module showed up without being asked for; it was probably in a <script> element
injectDependencies(module);
execQ.push(module);
checkComplete();
}
setArrived(module);
if(!isFunction(def) && !deps.length){
module.result = def;
finishExec(module);
}
return module;
},
runDefQ = function(referenceModule, mids){
// defQ is an array of [id, dependencies, factory]
// mids (if any) is a vector of mids given by a combo service
var definedModules = [],
module, args;
while(defQ.length){
args = defQ.shift();
mids && (args[0]= mids.shift());
// explicit define indicates possible multiple modules in a single file; delay injecting dependencies until defQ fully
// processed since modules earlier in the queue depend on already-arrived modules that are later in the queue
// TODO: what if no args[0] and no referenceModule
module = (args[0] && getModule(args[0])) || referenceModule;
definedModules.push([module, args[1], args[2]]);
}
consumePendingCacheInsert(referenceModule);
forEach(definedModules, function(args){
injectDependencies(defineModule.apply(null, args));
});
};
}
var timerId = 0,
clearTimer = noop,
startTimer = noop;
Iif(has("dojo-timeout-api")){
// Timer machinery that monitors how long the loader is waiting and signals an error when the timer runs out.
clearTimer = function(){
timerId && clearTimeout(timerId);
timerId = 0;
};
startTimer = function(){
clearTimer();
if(req.waitms){
timerId = window.setTimeout(function(){
clearTimer();
signal(error, makeError("timeout", waiting));
}, req.waitms);
}
};
}
Iif (has("dom")) {
// Test for IE's different way of signaling when scripts finish loading. Note that according to
// http://bugs.dojotoolkit.org/ticket/15096#comment:14, IE9 also needs to follow the
// IE specific code path even though it has an addEventListener() method.
// Unknown if special path needed on IE10+, which also has a document.attachEvent() method.
// Should evaluate to false for Opera and Windows 8 apps, even though they document.attachEvent()
// is defined in both those environments.
has.add("ie-event-behavior", doc.attachEvent && typeof Windows === "undefined" &&
(typeof opera === "undefined" || opera.toString() != "[object Opera]"));
}
Iif(has("dom") && (has("dojo-inject-api") || has("dojo-dom-ready-api"))){
var domOn = function(node, eventName, ieEventName, handler){
// Add an event listener to a DOM node using the API appropriate for the current browser;
// return a function that will disconnect the listener.
if(!has("ie-event-behavior")){
node.addEventListener(eventName, handler, false);
return function(){
node.removeEventListener(eventName, handler, false);
};
}else{
node.attachEvent(ieEventName, handler);
return function(){
node.detachEvent(ieEventName, handler);
};
}
},
windowOnLoadListener = domOn(window, "load", "onload", function(){
req.pageLoaded = 1;
doc.readyState!="complete" && (doc.readyState = "complete");
windowOnLoadListener();
});
if(has("dojo-inject-api")){
// if the loader is on the page, there must be at least one script element
// getting its parent and then doing insertBefore solves the "Operation Aborted"
// error in IE from appending to a node that isn't properly closed; see
// dojo/tests/_base/loader/requirejs/simple-badbase.html for an example
// don't use scripts with type dojo/... since these may be removed; see #15809
// prefer to use the insertPoint computed during the config sniff in case a script is removed; see #16958
var scripts = doc.getElementsByTagName("script"),
i = 0,
script;
while(!insertPointSibling){
if(!/^dojo/.test((script = scripts[i++]) && script.type)){
insertPointSibling= script;
}
}
req.injectUrl = function(url, callback, owner){
// insert a script element to the insert-point element with src=url;
// apply callback upon detecting the script has loaded.
var node = owner.node = doc.createElement("script"),
onLoad = function(e){
e = e || window.event;
var node = e.target || e.srcElement;
if(e.type === "load" || /complete|loaded/.test(node.readyState)){
loadDisconnector();
errorDisconnector();
callback && callback();
}
},
loadDisconnector = domOn(node, "load", "onreadystatechange", onLoad),
errorDisconnector = domOn(node, "error", "onerror", function(e){
loadDisconnector();
errorDisconnector();
signal(error, makeError("scriptError", [url, e]));
});
node.type = "text/javascript";
node.charset = "utf-8";
node.src = url;
insertPointSibling.parentNode.insertBefore(node, insertPointSibling);
return node;
};
}
}
Eif(has("dojo-log-api")){
req.log = function(){
try{
for(var i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
}catch(e){}
};
}else{
req.log = noop;
}
Eif(has("dojo-trace-api")){
var trace = req.trace = function(
group, // the trace group to which this application belongs
args // the contents of the trace
){
///
// Tracing interface by group.
//
// Sends the contents of args to the console iff (req.trace.on && req.trace[group])
Iif(trace.on && trace.group[group]){
signal("trace", [group, args]);
for(var arg, dump = [], text= "trace:" + group + (args.length ? (":" + args[0]) : ""), i= 1; i<args.length;){
arg = args[i++];
if(isString(arg)){
text += ", " + arg;
}else{
dump.push(arg);
}
}
req.log(text);
dump.length && dump.push(".");
req.log.apply(req, dump);
}
};
mix(trace, {
on:1,
group:{},
set:function(group, value){
Iif(isString(group)){
trace.group[group]= value;
}else{
mix(trace.group, group);
}
}
});
trace.set(mix(mix(mix({}, defaultConfig.trace), userConfig.trace), dojoSniffConfig.trace));
on("config", function(config){
config.trace && trace.set(config.trace);
});
}else{
req.trace = noop;
}
var def = function(
mid, //(commonjs.moduleId, optional)
dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before running factory
factory //(any)
){
///
// Advises the loader of a module factory. //Implements http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition.
///
//note
// CommonJS factory scan courtesy of http://requirejs.org
var arity = arguments.length,
defaultDeps = ["require", "exports", "module"],
// the predominate signature...
args = [0, mid, dependencies];
if(arity==1){
args = [0, (isFunction(mid) ? defaultDeps : []), mid];
}else Iif(arity==2 && isString(mid)){
args = [mid, (isFunction(dependencies) ? defaultDeps : []), dependencies];
}else if(arity==3){
args = [mid, dependencies, factory];
}
if(has("dojo-amd-factory-scan") && args[1]===defaultDeps){
args[2].toString()
.replace(/(\/\*([\s\S]*?)\*\/|\/\/(.*)$)/mg, "")
.replace(/require\(["']([\w\!\-_\.\/]+)["']\)/g, function(match, dep){
args[1].push(dep);
});
}
req.trace("loader-define", args.slice(0, 2));
var targetModule = args[0] && getModule(args[0]),
module;
if(targetModule && !waiting[targetModule.mid]){
// given a mid that hasn't been requested; therefore, defined through means other than injecting
// consequent to a require() or define() application; examples include defining modules on-the-fly
// due to some code path or including a module in a script element. In any case,
// there is no callback waiting to finish processing and nothing to trigger the defQ and the
// dependencies are never requested; therefore, do it here.
injectDependencies(defineModule(targetModule, args[1], args[2]));
}else Eif(!has("ie-event-behavior") || !has("host-browser") || injectingCachedModule){
// not IE path: anonymous module and therefore must have been injected; therefore, onLoad will fire immediately
// after script finishes being evaluated and the defQ can be run from that callback to detect the module id
defQ.push(args);
}else{
// IE path: possibly anonymous module and therefore injected; therefore, cannot depend on 1-to-1,
// in-order exec of onLoad with script eval (since it's IE) and must manually detect here
targetModule = targetModule || injectingModule;
if(!targetModule){
for(mid in waiting){
module = modules[mid];
if(module && module.node && module.node.readyState === 'interactive'){
targetModule = module;
break;
}
}
if(has("dojo-combo-api") && !targetModule){
for(var i = 0; i<combosPending.length; i++){
targetModule = combosPending[i];
if(targetModule.node && targetModule.node.readyState === 'interactive'){
break;
}
targetModule= 0;
}
}
}
if(has("dojo-combo-api") && isArray(targetModule)){
injectDependencies(defineModule(getModule(targetModule.shift()), args[1], args[2]));
if(!targetModule.length){
combosPending.splice(i, 1);
}
}else if(targetModule){
consumePendingCacheInsert(targetModule);
injectDependencies(defineModule(targetModule, args[1], args[2]));
}else{
signal(error, makeError("ieDefineFailed", args[0]));
}
checkComplete();
}
};
def.amd = {
vendor:"dojotoolkit.org"
};
Iif(has("dojo-requirejs-api")){
req.def = def;
}
// allow config to override default implementation of named functions; this is useful for
// non-browser environments, e.g., overriding injectUrl, getText, log, etc. in node.js, Rhino, etc.
// also useful for testing and monkey patching loader
mix(mix(req, defaultConfig.loaderPatch), userConfig.loaderPatch);
// now that req is fully initialized and won't change, we can hook it up to the error signal
on(error, function(arg){
try{
console.error(arg);
Eif(arg instanceof Error){
for(var p in arg){
console.log(p + ":", arg[p]);
}
console.log(".");
}
}catch(e){}
});
// always publish these
mix(req, {
uid:uid,
cache:cache,
packs:packs
});
Eif(has("dojo-publish-privates")){
mix(req, {
// these may be interesting to look at when debugging
paths:paths,
aliases:aliases,
modules:modules,
legacyMode:legacyMode,
execQ:execQ,
defQ:defQ,
waiting:waiting,
// these are used for testing
// TODO: move testing infrastructure to a different has feature
packs:packs,
mapProgs:mapProgs,
pathsMapProg:pathsMapProg,
listenerQueues:listenerQueues,
// these are used by the builder (at least)
computeMapProg:computeMapProg,
computeAliases:computeAliases,
runMapProg:runMapProg,
compactPath:compactPath,
getModuleInfo:getModuleInfo_
});
}
// the loader can be defined exactly once; look for global define which is the symbol AMD loaders are
// *required* to define (as opposed to require, which is optional)
if(global.define){
Eif(has("dojo-log-api")){
signal(error, makeError("defineAlreadyDefined", 0));
}
return;
}else{
global.define = def;
global.require = req;
Eif(has("host-node")){
require = req;
}
}
Iif(has("dojo-combo-api") && req.combo && req.combo.plugins){
var plugins = req.combo.plugins,
pluginName;
for(pluginName in plugins){
mix(mix(getModule(pluginName), plugins[pluginName]), {isCombo:1, executed:"executed", load:1});
}
}
Eif(has("dojo-config-api")){
forEach(delayedModuleConfig, function(c){ config(c); });
var bootDeps = dojoSniffConfig.deps || userConfig.deps || defaultConfig.deps,
bootCallback = dojoSniffConfig.callback || userConfig.callback || defaultConfig.callback;
req.boot = (bootDeps || bootCallback) ? [bootDeps || [], bootCallback] : 0;
}
Eif(!has("dojo-built")){
!req.async && req(["dojo"]);
req.boot && req.apply(null, req.boot);
}
})
//>>excludeStart("replaceLoaderConfig", kwArgs.replaceLoaderConfig);
(
// userConfig
(function(){
// make sure we're looking at global dojoConfig etc.
return this.dojoConfig || this.djConfig || this.require || {};
})(),
// defaultConfig
{
// the default configuration for a browser; this will be modified by other environments
hasCache:{
"host-browser":1,
"dom":1,
"dojo-amd-factory-scan":1,
"dojo-loader":1,
"dojo-has-api":1,
"dojo-inject-api":1,
"dojo-timeout-api":1,
"dojo-trace-api":1,
"dojo-log-api":1,
"dojo-dom-ready-api":1,
"dojo-publish-privates":1,
"dojo-config-api":1,
"dojo-sniff":1,
"dojo-sync-loader":1,
"dojo-test-sniff":1,
"config-deferredInstrumentation":1,
"config-useDeferredInstrumentation":"report-unhandled-rejections",
"config-tlmSiblingOfDojo":0
},
packages:[{
// note: like v1.6-, this bootstrap computes baseUrl to be the dojo directory
name:'dojo',
location:'.'
},{
name:'tests',
location:'./tests'
},{
name:'dijit',
location:'../dijit'
},{
name:'build',
location:'../util/build'
},{
name:'doh',
location:'../util/doh'
},{
name:'dojox',
location:'../dojox'
},{
name:'demos',
location:'../demos'
}],
trace:{
// these are listed so it's simple to turn them on/off while debugging loading
"loader-inject":0,
"loader-define":0,
"loader-exec-module":0,
"loader-run-factory":0,
"loader-finish-exec":0,
"loader-define-module":0,
"loader-circular-dependency":0,
"loader-define-nonmodule":0
},
async:0,
waitSeconds:15
}
);
//>>excludeEnd("replaceLoaderConfig")
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | 1 1 1 | var profile = (function(){
var testResourceRe = /^dojo\/tests\//,
copyOnly = function(filename, mid){
var list = {
"dojo/dojo.profile":1,
"dojo/package.json":1,
"dojo/OpenAjax":1,
"dojo/tests":1,
// these are test modules that are not intended to ever be built
"dojo/tests/_base/loader/requirejs/requirejs-setup":1,
"dojo/tests/_base/loader/requirejs/dataMain":1,
"dojo/tests/_base/loader/requirejs/depoverlap":1,
"dojo/tests/_base/loader/requirejs/simple-tests":1,
"dojo/tests/_base/loader/requirejs/relative/relative-tests":1,
"dojo/tests/_base/loader/requirejs/exports/exports-tests":1
};
return (mid in list) ||
/^dojo\/_base\/config\w+$/.test(mid) ||
(/^dojo\/resources\//.test(mid) && !/\.css$/.test(filename)) ||
/(png|jpg|jpeg|gif|tiff)$/.test(filename) ||
/built\-i18n\-test\/152\-build/.test(mid);
};
return {
resourceTags:{
test: function(filename, mid){
return testResourceRe.test(mid) || mid=="dojo/tests" || mid=="dojo/robot" || mid=="dojo/robotx";
},
copyOnly: function(filename, mid){
return copyOnly(filename, mid);
},
amd: function(filename, mid){
return !testResourceRe.test(mid) && !copyOnly(filename, mid) && /\.js$/.test(filename);
}
}
};
})();
|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | 1 1 | define(["exports", "./sniff", "./_base/lang", "./dom", "./dom-style", "./dom-prop"], function(exports, has, lang, dom, style, prop){ // module: // dojo/dom-attr // summary: // This module defines the core dojo DOM attributes API. // TODOC: summary not showing up in output see https://github.com/csnover/js-doc-parse/issues/42 // ============================= // Element attribute Functions // ============================= // This module will be obsolete soon. Use dojo/prop instead. // dojo/dom-attr.get() should conform to http://www.w3.org/TR/DOM-Level-2-Core/ // attribute-related functions (to be obsolete soon) var forcePropNames = { innerHTML: 1, className: 1, htmlFor: has("ie"), value: 1 }, attrNames = { // original attribute names classname: "class", htmlfor: "for", // for IE tabindex: "tabIndex", readonly: "readOnly" }; function _hasAttr(node, name){ var attr = node.getAttributeNode && node.getAttributeNode(name); return attr && attr.specified; // Boolean } // There is a difference in the presence of certain properties and their default values // between browsers. For example, on IE "disabled" is present on all elements, // but it is value is "false"; "tabIndex" of <div> returns 0 by default on IE, yet other browsers // can return -1. exports.has = function hasAttr(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Returns true if the requested attribute is specified on the // given element, and false otherwise. // node: DOMNode|String // id or reference to the element to check // name: String // the name of the attribute // returns: Boolean // true if the requested attribute is specified on the // given element, and false otherwise var lc = name.toLowerCase(); return forcePropNames[prop.names[lc] || name] || _hasAttr(dom.byId(node), attrNames[lc] || name); // Boolean }; exports.get = function getAttr(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Gets an attribute on an HTML element. // description: // Handles normalized getting of attributes on DOM Nodes. // node: DOMNode|String // id or reference to the element to get the attribute on // name: String // the name of the attribute to get. // returns: // the value of the requested attribute or null if that attribute does not have a specified or // default value; // // example: // | // get the current value of the "foo" attribute on a node // | require(["dojo/dom-attr", "dojo/dom"], function(domAttr, dom){ // | domAttr.get(dom.byId("nodeId"), "foo"); // | // or we can just pass the id: // | domAttr.get("nodeId", "foo"); // | }); // | node = dom.byId(node); var lc = name.toLowerCase(), propName = prop.names[lc] || name, forceProp = forcePropNames[propName], value = node[propName]; // should we access this attribute via a property or via getAttribute()? if(forceProp && typeof value != "undefined"){ // node's property return value; // Anything } if(propName != "href" && (typeof value == "boolean" || lang.isFunction(value))){ // node's property return value; // Anything } // node's attribute // we need _hasAttr() here to guard against IE returning a default value var attrName = attrNames[lc] || name; return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything }; exports.set = function setAttr(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){ // summary: // Sets an attribute on an HTML element. // description: // Handles normalized setting of attributes on DOM Nodes. // // When passing functions as values, note that they will not be // directly assigned to slots on the node, but rather the default // behavior will be removed and the new behavior will be added // using `dojo.connect()`, meaning that event handler properties // will be normalized and that some caveats with regards to // non-standard behaviors for onsubmit apply. Namely that you // should cancel form submission using `dojo.stopEvent()` on the // passed event object instead of returning a boolean value from // the handler itself. // node: DOMNode|String // id or reference to the element to set the attribute on // name: String|Object // the name of the attribute to set, or a hash of key-value pairs to set. // value: String? // the value to set for the attribute, if the name is a string. // returns: // the DOM node // // example: // | // use attr() to set the tab index // | require(["dojo/dom-attr"], function(domAttr){ // | domAttr.set("nodeId", "tabIndex", 3); // | }); // // example: // Set multiple values at once, including event handlers: // | require(["dojo/dom-attr"], // | function(domAttr){ // | domAttr.set("formId", { // | "foo": "bar", // | "tabIndex": -1, // | "method": "POST" // | } // | }); node = dom.byId(node); if(arguments.length == 2){ // inline'd type check // the object form of setter: the 2nd argument is a dictionary for(var x in name){ exports.set(node, x, name[x]); } return node; // DomNode } var lc = name.toLowerCase(), propName = prop.names[lc] || name, forceProp = forcePropNames[propName]; if(propName == "style" && typeof value != "string"){ // inline'd type check // special case: setting a style style.set(node, value); return node; // DomNode } if(forceProp || typeof value == "boolean" || lang.isFunction(value)){ return prop.set(node, name, value); } // node's attribute node.setAttribute(attrNames[lc] || name, value); return node; // DomNode }; exports.remove = function removeAttr(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Removes an attribute from an HTML element. // node: DOMNode|String // id or reference to the element to remove the attribute from // name: String // the name of the attribute to remove dom.byId(node).removeAttribute(attrNames[name.toLowerCase()] || name); }; exports.getNodeProp = function getNodeProp(/*DomNode|String*/ node, /*String*/ name){ // summary: // Returns an effective value of a property or an attribute. // node: DOMNode|String // id or reference to the element to remove the attribute from // name: String // the name of the attribute // returns: // the value of the attribute node = dom.byId(node); var lc = name.toLowerCase(), propName = prop.names[lc] || name; if((propName in node) && propName != "href"){ // node's property return node[propName]; // Anything } // node's attribute var attrName = attrNames[lc] || name; return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | 1 1 | define(["./_base/lang", "./_base/array", "./dom"], function(lang, array, dom){ // module: // dojo/dom-class var className = "className"; /* Part I of classList-based implementation is preserved here for posterity var classList = "classList"; has.add("dom-classList", function(){ return classList in document.createElement("p"); }); */ // ============================= // (CSS) Class Functions // ============================= var cls, // exports object spaces = /\s+/, a1 = [""]; function str2array(s){ if(typeof s == "string" || s instanceof String){ if(s && !spaces.test(s)){ a1[0] = s; return a1; } var a = s.split(spaces); if(a.length && !a[0]){ a.shift(); } if(a.length && !a[a.length - 1]){ a.pop(); } return a; } // assumed to be an array if(!s){ return []; } return array.filter(s, function(x){ return x; }); } /* Part II of classList-based implementation is preserved here for posterity if(has("dom-classList")){ // new classList version cls = { contains: function containsClass(node, classStr){ var clslst = classStr && dom.byId(node)[classList]; return clslst && clslst.contains(classStr); // Boolean }, add: function addClass(node, classStr){ node = dom.byId(node); classStr = str2array(classStr); for(var i = 0, len = classStr.length; i < len; ++i){ node[classList].add(classStr[i]); } }, remove: function removeClass(node, classStr){ node = dom.byId(node); if(classStr === undefined){ node[className] = ""; }else{ classStr = str2array(classStr); for(var i = 0, len = classStr.length; i < len; ++i){ node[classList].remove(classStr[i]); } } }, replace: function replaceClass(node, addClassStr, removeClassStr){ node = dom.byId(node); if(removeClassStr === undefined){ node[className] = ""; }else{ removeClassStr = str2array(removeClassStr); for(var i = 0, len = removeClassStr.length; i < len; ++i){ node[classList].remove(removeClassStr[i]); } } addClassStr = str2array(addClassStr); for(i = 0, len = addClassStr.length; i < len; ++i){ node[classList].add(addClassStr[i]); } }, toggle: function toggleClass(node, classStr, condition){ node = dom.byId(node); if(condition === undefined){ classStr = str2array(classStr); for(var i = 0, len = classStr.length; i < len; ++i){ node[classList].toggle(classStr[i]); } }else{ cls[condition ? "add" : "remove"](node, classStr); } return condition; // Boolean } } } */ // regular DOM version var fakeNode = {}; // for effective replacement cls = { // summary: // This module defines the core dojo DOM class API. contains: function containsClass(/*DomNode|String*/ node, /*String*/ classStr){ // summary: // Returns whether or not the specified classes are a portion of the // class list currently applied to the node. // node: String|DOMNode // String ID or DomNode reference to check the class for. // classStr: String // A string class name to look for. // example: // Do something if a node with id="someNode" has class="aSillyClassName" present // | if(dojo.hasClass("someNode","aSillyClassName")){ ... } return ((" " + dom.byId(node)[className] + " ").indexOf(" " + classStr + " ") >= 0); // Boolean }, add: function addClass(/*DomNode|String*/ node, /*String|Array*/ classStr){ // summary: // Adds the specified classes to the end of the class list on the // passed node. Will not re-apply duplicate classes. // // node: String|DOMNode // String ID or DomNode reference to add a class string too // // classStr: String|Array // A String class name to add, or several space-separated class names, // or an array of class names. // // example: // Add a class to some node: // | require(["dojo/dom-class"], function(domClass){ // | domClass.add("someNode", "anewClass"); // | }); // // example: // Add two classes at once: // | require(["dojo/dom-class"], function(domClass){ // | domClass.add("someNode", "firstClass secondClass"); // | }); // // example: // Add two classes at once (using array): // | require(["dojo/dom-class"], function(domClass){ // | domClass.add("someNode", ["firstClass", "secondClass"]); // | }); // // example: // Available in `dojo/NodeList` for multiple additions // | require(["dojo/query"], function(query){ // | query("ul > li").addClass("firstLevel"); // | }); node = dom.byId(node); classStr = str2array(classStr); var cls = node[className], oldLen; cls = cls ? " " + cls + " " : " "; oldLen = cls.length; for(var i = 0, len = classStr.length, c; i < len; ++i){ c = classStr[i]; if(c && cls.indexOf(" " + c + " ") < 0){ cls += c + " "; } } if(oldLen < cls.length){ node[className] = cls.substr(1, cls.length - 2); } }, remove: function removeClass(/*DomNode|String*/ node, /*String|Array?*/ classStr){ // summary: // Removes the specified classes from node. No `contains()` // check is required. // // node: String|DOMNode // String ID or DomNode reference to remove the class from. // // classStr: String|Array // An optional String class name to remove, or several space-separated // class names, or an array of class names. If omitted, all class names // will be deleted. // // example: // Remove a class from some node: // | require(["dojo/dom-class"], function(domClass){ // | domClass.remove("someNode", "firstClass"); // | }); // // example: // Remove two classes from some node: // | require(["dojo/dom-class"], function(domClass){ // | domClass.remove("someNode", "firstClass secondClass"); // | }); // // example: // Remove two classes from some node (using array): // | require(["dojo/dom-class"], function(domClass){ // | domClass.remove("someNode", ["firstClass", "secondClass"]); // | }); // // example: // Remove all classes from some node: // | require(["dojo/dom-class"], function(domClass){ // | domClass.remove("someNode"); // | }); // // example: // Available in `dojo/NodeList` for multiple removal // | require(["dojo/query"], function(query){ // | query("ul > li").removeClass("foo"); // | }); node = dom.byId(node); var cls; if(classStr !== undefined){ classStr = str2array(classStr); cls = " " + node[className] + " "; for(var i = 0, len = classStr.length; i < len; ++i){ cls = cls.replace(" " + classStr[i] + " ", " "); } cls = lang.trim(cls); }else{ cls = ""; } if(node[className] != cls){ node[className] = cls; } }, replace: function replaceClass(/*DomNode|String*/ node, /*String|Array*/ addClassStr, /*String|Array?*/ removeClassStr){ // summary: // Replaces one or more classes on a node if not present. // Operates more quickly than calling dojo.removeClass and dojo.addClass // // node: String|DOMNode // String ID or DomNode reference to remove the class from. // // addClassStr: String|Array // A String class name to add, or several space-separated class names, // or an array of class names. // // removeClassStr: String|Array? // A String class name to remove, or several space-separated class names, // or an array of class names. // // example: // | require(["dojo/dom-class"], function(domClass){ // | domClass.replace("someNode", "add1 add2", "remove1 remove2"); // | }); // // example: // Replace all classes with addMe // | require(["dojo/dom-class"], function(domClass){ // | domClass.replace("someNode", "addMe"); // | }); // // example: // Available in `dojo/NodeList` for multiple toggles // | require(["dojo/query"], function(query){ // | query(".findMe").replaceClass("addMe", "removeMe"); // | }); node = dom.byId(node); fakeNode[className] = node[className]; cls.remove(fakeNode, removeClassStr); cls.add(fakeNode, addClassStr); if(node[className] !== fakeNode[className]){ node[className] = fakeNode[className]; } }, toggle: function toggleClass(/*DomNode|String*/ node, /*String|Array*/ classStr, /*Boolean?*/ condition){ // summary: // Adds a class to node if not present, or removes if present. // Pass a boolean condition if you want to explicitly add or remove. // Returns the condition that was specified directly or indirectly. // // node: String|DOMNode // String ID or DomNode reference to toggle a class string // // classStr: String|Array // A String class name to toggle, or several space-separated class names, // or an array of class names. // // condition: // If passed, true means to add the class, false means to remove. // Otherwise dojo.hasClass(node, classStr) is used to detect the class presence. // // example: // | require(["dojo/dom-class"], function(domClass){ // | domClass.toggle("someNode", "hovered"); // | }); // // example: // Forcefully add a class // | require(["dojo/dom-class"], function(domClass){ // | domClass.toggle("someNode", "hovered", true); // | }); // // example: // Available in `dojo/NodeList` for multiple toggles // | require(["dojo/query"], function(query){ // | query(".toggleMe").toggleClass("toggleMe"); // | }); node = dom.byId(node); if(condition === undefined){ classStr = str2array(classStr); for(var i = 0, len = classStr.length, c; i < len; ++i){ c = classStr[i]; cls[cls.contains(node, c) ? "remove" : "add"](node, c); } }else{ cls[condition ? "add" : "remove"](node, classStr); } return condition; // Boolean } }; return cls; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | 1 1 1 1 1 | define(["exports", "./_base/kernel", "./sniff", "./_base/window", "./dom", "./dom-attr"], function(exports, dojo, has, win, dom, attr){ // module: // dojo/dom-construct // summary: // This module defines the core dojo DOM construction API. // TODOC: summary not showing up in output, see https://github.com/csnover/js-doc-parse/issues/42 // support stuff for toDom() var tagWrap = { option: ["select"], tbody: ["table"], thead: ["table"], tfoot: ["table"], tr: ["table", "tbody"], td: ["table", "tbody", "tr"], th: ["table", "thead", "tr"], legend: ["fieldset"], caption: ["table"], colgroup: ["table"], col: ["table", "colgroup"], li: ["ul"] }, reTag = /<\s*([\w\:]+)/, masterNode = {}, masterNum = 0, masterName = "__" + dojo._scopeName + "ToDomId"; // generate start/end tag strings to use // for the injection for each special tag wrap case. for(var param in tagWrap){ if(tagWrap.hasOwnProperty(param)){ var tw = tagWrap[param]; tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">"; tw.post = "</" + tw.reverse().join("></") + ">"; // the last line is destructive: it reverses the array, // but we don't care at this point } } var html5domfix; if(has("ie") <= 8){ html5domfix = function(doc){ doc.__dojo_html5_tested = "yes"; var div = create('div', {innerHTML: "<nav>a</nav>", style: {visibility: "hidden"}}, doc.body); if(div.childNodes.length !== 1){ ('abbr article aside audio canvas details figcaption figure footer header ' + 'hgroup mark meter nav output progress section summary time video').replace( /\b\w+\b/g, function(n){ doc.createElement(n); } ); } destroy(div); } } function _insertBefore(/*DomNode*/ node, /*DomNode*/ ref){ var parent = ref.parentNode; if(parent){ parent.insertBefore(node, ref); } } function _insertAfter(/*DomNode*/ node, /*DomNode*/ ref){ // summary: // Try to insert node after ref var parent = ref.parentNode; if(parent){ if(parent.lastChild == ref){ parent.appendChild(node); }else{ parent.insertBefore(node, ref.nextSibling); } } } exports.toDom = function toDom(frag, doc){ // summary: // instantiates an HTML fragment returning the corresponding DOM. // frag: String // the HTML fragment // doc: DocumentNode? // optional document to use when creating DOM nodes, defaults to // dojo/_base/window.doc if not specified. // returns: // Document fragment, unless it's a single node in which case it returns the node itself // example: // Create a table row: // | require(["dojo/dom-construct"], function(domConstruct){ // | var tr = domConstruct.toDom("<tr><td>First!</td></tr>"); // | }); doc = doc || win.doc; var masterId = doc[masterName]; if(!masterId){ doc[masterName] = masterId = ++masterNum + ""; masterNode[masterId] = doc.createElement("div"); } if(has("ie") <= 8){ if(!doc.__dojo_html5_tested && doc.body){ html5domfix(doc); } } // make sure the frag is a string. frag += ""; // find the starting tag, and get node wrapper var match = frag.match(reTag), tag = match ? match[1].toLowerCase() : "", master = masterNode[masterId], wrap, i, fc, df; if(match && tagWrap[tag]){ wrap = tagWrap[tag]; master.innerHTML = wrap.pre + frag + wrap.post; for(i = wrap.length; i; --i){ master = master.firstChild; } }else{ master.innerHTML = frag; } // one node shortcut => return the node itself if(master.childNodes.length == 1){ return master.removeChild(master.firstChild); // DOMNode } // return multiple nodes as a document fragment df = doc.createDocumentFragment(); while((fc = master.firstChild)){ // intentional assignment df.appendChild(fc); } return df; // DocumentFragment }; exports.place = function place(/*DOMNode|String*/ node, /*DOMNode|String*/ refNode, /*String|Number?*/ position){ // summary: // Attempt to insert node into the DOM, choosing from various positioning options. // Returns the first argument resolved to a DOM node. // node: DOMNode|String // id or node reference, or HTML fragment starting with "<" to place relative to refNode // refNode: DOMNode|String // id or node reference to use as basis for placement // position: String|Number? // string noting the position of node relative to refNode or a // number indicating the location in the childNodes collection of refNode. // Accepted string values are: // // - before // - after // - replace // - only // - first // - last // // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode, // "only" replaces all children. position defaults to "last" if not specified // returns: DOMNode // Returned values is the first argument resolved to a DOM node. // // .place() is also a method of `dojo/NodeList`, allowing `dojo/query` node lookups. // example: // Place a node by string id as the last child of another node by string id: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.place("someNode", "anotherNode"); // | }); // example: // Place a node by string id before another node by string id // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.place("someNode", "anotherNode", "before"); // | }); // example: // Create a Node, and place it in the body element (last child): // | require(["dojo/dom-construct", "dojo/_base/window" // | ], function(domConstruct, win){ // | domConstruct.place("<div></div>", win.body()); // | }); // example: // Put a new LI as the first child of a list by id: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.place("<li></li>", "someUl", "first"); // | }); refNode = dom.byId(refNode); if(typeof node == "string"){ // inline'd type check node = /^\s*</.test(node) ? exports.toDom(node, refNode.ownerDocument) : dom.byId(node); } if(typeof position == "number"){ // inline'd type check var cn = refNode.childNodes; if(!cn.length || cn.length <= position){ refNode.appendChild(node); }else{ _insertBefore(node, cn[position < 0 ? 0 : position]); } }else{ switch(position){ case "before": _insertBefore(node, refNode); break; case "after": _insertAfter(node, refNode); break; case "replace": refNode.parentNode.replaceChild(node, refNode); break; case "only": exports.empty(refNode); refNode.appendChild(node); break; case "first": if(refNode.firstChild){ _insertBefore(node, refNode.firstChild); break; } // else fallthrough... default: // aka: last refNode.appendChild(node); } } return node; // DomNode }; var create = exports.create = function create(/*DOMNode|String*/ tag, /*Object*/ attrs, /*DOMNode|String?*/ refNode, /*String?*/ pos){ // summary: // Create an element, allowing for optional attribute decoration // and placement. // description: // A DOM Element creation function. A shorthand method for creating a node or // a fragment, and allowing for a convenient optional attribute setting step, // as well as an optional DOM placement reference. // // Attributes are set by passing the optional object through `dojo.setAttr`. // See `dojo.setAttr` for noted caveats and nuances, and API if applicable. // // Placement is done via `dojo.place`, assuming the new node to be the action // node, passing along the optional reference node and position. // tag: DOMNode|String // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"), // or an existing DOM node to process. // attrs: Object // An object-hash of attributes to set on the newly created node. // Can be null, if you don't want to set any attributes/styles. // See: `dojo.setAttr` for a description of available attributes. // refNode: DOMNode|String? // Optional reference node. Used by `dojo.place` to place the newly created // node somewhere in the dom relative to refNode. Can be a DomNode reference // or String ID of a node. // pos: String? // Optional positional reference. Defaults to "last" by way of `dojo.place`, // though can be set to "first","after","before","last", "replace" or "only" // to further control the placement of the new node relative to the refNode. // 'refNode' is required if a 'pos' is specified. // example: // Create a DIV: // | require(["dojo/dom-construct"], function(domConstruct){ // | var n = domConstruct.create("div"); // | }); // // example: // Create a DIV with content: // | require(["dojo/dom-construct"], function(domConstruct){ // | var n = domConstruct.create("div", { innerHTML:"<p>hi</p>" }); // | }); // // example: // Place a new DIV in the BODY, with no attributes set // | require(["dojo/dom-construct"], function(domConstruct){ // | var n = domConstruct.create("div", null, dojo.body()); // | }); // // example: // Create an UL, and populate it with LI's. Place the list as the first-child of a // node with id="someId": // | require(["dojo/dom-construct", "dojo/_base/array"], // | function(domConstruct, arrayUtil){ // | var ul = domConstruct.create("ul", null, "someId", "first"); // | var items = ["one", "two", "three", "four"]; // | arrayUtil.forEach(items, function(data){ // | domConstruct.create("li", { innerHTML: data }, ul); // | }); // | }); // // example: // Create an anchor, with an href. Place in BODY: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body()); // | }); var doc = win.doc; if(refNode){ refNode = dom.byId(refNode); doc = refNode.ownerDocument; } if(typeof tag == "string"){ // inline'd type check tag = doc.createElement(tag); } if(attrs){ attr.set(tag, attrs); } if(refNode){ exports.place(tag, refNode, pos); } return tag; // DomNode }; function _empty(/*DomNode*/ node){ if(node.canHaveChildren){ try{ // fast path node.innerHTML = ""; return; }catch(e){ // innerHTML is readOnly (e.g. TABLE (sub)elements in quirks mode) // Fall through (saves bytes) } } // SVG/strict elements don't support innerHTML/canHaveChildren, and OBJECT/APPLET elements in quirks node have canHaveChildren=false for(var c; c = node.lastChild;){ // intentional assignment _destroy(c, node); // destroy is better than removeChild so TABLE subelements are removed in proper order } } exports.empty = function empty(/*DOMNode|String*/ node){ // summary: // safely removes all children of the node. // node: DOMNode|String // a reference to a DOM node or an id. // example: // Destroy node's children byId: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.empty("someId"); // | }); _empty(dom.byId(node)); }; function _destroy(/*DomNode*/ node, /*DomNode*/ parent){ // in IE quirks, node.canHaveChildren can be false but firstChild can be non-null (OBJECT/APPLET) if(node.firstChild){ _empty(node); } if(parent){ // removeNode(false) doesn't leak in IE 6+, but removeChild() and removeNode(true) are known to leak under IE 8- while 9+ is TBD. // In IE quirks mode, PARAM nodes as children of OBJECT/APPLET nodes have a removeNode method that does nothing and // the parent node has canHaveChildren=false even though removeChild correctly removes the PARAM children. // In IE, SVG/strict nodes don't have a removeNode method nor a canHaveChildren boolean. has("ie") && parent.canHaveChildren && "removeNode" in node ? node.removeNode(false) : parent.removeChild(node); } } var destroy = exports.destroy = function destroy(/*DOMNode|String*/ node){ // summary: // Removes a node from its parent, clobbering it and all of its // children. // // description: // Removes a node from its parent, clobbering it and all of its // children. Function only works with DomNodes, and returns nothing. // // node: DOMNode|String // A String ID or DomNode reference of the element to be destroyed // // example: // Destroy a node byId: // | require(["dojo/dom-construct"], function(domConstruct){ // | domConstruct.destroy("someId"); // | }); node = dom.byId(node); if(!node){ return; } _destroy(node, node.parentNode); }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | 1 1 | define(["./_base/lang", "./dom", "./io-query", "./json"], function(lang, dom, ioq, json){ // module: // dojo/dom-form function setValue(/*Object*/ obj, /*String*/ name, /*String*/ value){ // summary: // For the named property in object, set the value. If a value // already exists and it is a string, convert the value to be an // array of values. // Skip it if there is no value if(value === null){ return; } var val = obj[name]; if(typeof val == "string"){ // inline'd type check obj[name] = [val, value]; }else if(lang.isArray(val)){ val.push(value); }else{ obj[name] = value; } } var exclude = "file|submit|image|reset|button"; var form = { // summary: // This module defines form-processing functions. fieldToObject: function fieldToObject(/*DOMNode|String*/ inputNode){ // summary: // Serialize a form field to a JavaScript object. // description: // Returns the value encoded in a form field as // as a string or an array of strings. Disabled form elements // and unchecked radio and checkboxes are skipped. Multi-select // elements are returned as an array of string values. // inputNode: DOMNode|String // returns: Object var ret = null; inputNode = dom.byId(inputNode); if(inputNode){ var _in = inputNode.name, type = (inputNode.type || "").toLowerCase(); if(_in && type && !inputNode.disabled){ if(type == "radio" || type == "checkbox"){ if(inputNode.checked){ ret = inputNode.value; } }else if(inputNode.multiple){ ret = []; var nodes = [inputNode.firstChild]; while(nodes.length){ for(var node = nodes.pop(); node; node = node.nextSibling){ if(node.nodeType == 1 && node.tagName.toLowerCase() == "option"){ if(node.selected){ ret.push(node.value); } }else{ if(node.nextSibling){ nodes.push(node.nextSibling); } if(node.firstChild){ nodes.push(node.firstChild); } break; } } } }else{ ret = inputNode.value; } } } return ret; // Object }, toObject: function formToObject(/*DOMNode|String*/ formNode){ // summary: // Serialize a form node to a JavaScript object. // description: // Returns the values encoded in an HTML form as // string properties in an object which it then returns. Disabled form // elements, buttons, and other non-value form elements are skipped. // Multi-select elements are returned as an array of string values. // formNode: DOMNode|String // example: // This form: // | <form id="test_form"> // | <input type="text" name="blah" value="blah"> // | <input type="text" name="no_value" value="blah" disabled> // | <input type="button" name="no_value2" value="blah"> // | <select type="select" multiple name="multi" size="5"> // | <option value="blah">blah</option> // | <option value="thud" selected>thud</option> // | <option value="thonk" selected>thonk</option> // | </select> // | </form> // // yields this object structure as the result of a call to // formToObject(): // // | { // | blah: "blah", // | multi: [ // | "thud", // | "thonk" // | ] // | }; var ret = {}, elems = dom.byId(formNode).elements; for(var i = 0, l = elems.length; i < l; ++i){ var item = elems[i], _in = item.name, type = (item.type || "").toLowerCase(); if(_in && type && exclude.indexOf(type) < 0 && !item.disabled){ setValue(ret, _in, form.fieldToObject(item)); if(type == "image"){ ret[_in + ".x"] = ret[_in + ".y"] = ret[_in].x = ret[_in].y = 0; } } } return ret; // Object }, toQuery: function formToQuery(/*DOMNode|String*/ formNode){ // summary: // Returns a URL-encoded string representing the form passed as either a // node or string ID identifying the form to serialize // formNode: DOMNode|String // returns: String return ioq.objectToQuery(form.toObject(formNode)); // String }, toJson: function formToJson(/*DOMNode|String*/ formNode, /*Boolean?*/ prettyPrint){ // summary: // Create a serialized JSON string from a form node or string // ID identifying the form to serialize // formNode: DOMNode|String // prettyPrint: Boolean? // returns: String return json.stringify(form.toObject(formNode), null, prettyPrint ? 4 : 0); // String } }; return form; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 | 1 1 1 1 | define(["./sniff", "./_base/window","./dom", "./dom-style"], function(has, win, dom, style){ // module: // dojo/dom-geometry // the result object var geom = { // summary: // This module defines the core dojo DOM geometry API. }; // Box functions will assume this model. // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode. // Can be set to change behavior of box setters. // can be either: // "border-box" // "content-box" (default) geom.boxModel = "content-box"; // We punt per-node box mode testing completely. // If anybody cares, we can provide an additional (optional) unit // that overrides existing code to include per-node box sensitivity. // Opera documentation claims that Opera 9 uses border-box in BackCompat mode. // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box. // IIRC, earlier versions of Opera did in fact use border-box. // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault. if(has("ie") /*|| has("opera")*/){ // client code may have to adjust if compatMode varies across iframes geom.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box"; } geom.getPadExtents = function getPadExtents(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // Returns object with special values specifically useful for node // fitting. // description: // Returns an object with `w`, `h`, `l`, `t` properties: // | l/t/r/b = left/top/right/bottom padding (respectively) // | w = the total of the left and right padding // | h = the total of the top and bottom padding // If 'node' has position, l/t forms the origin for child nodes. // The w/h are used for calculating boxes. // Normally application code will not need to invoke this // directly, and will use the ...box... functions instead. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue, l = px(node, s.paddingLeft), t = px(node, s.paddingTop), r = px(node, s.paddingRight), b = px(node, s.paddingBottom); return {l: l, t: t, r: r, b: b, w: l + r, h: t + b}; }; var none = "none"; geom.getBorderExtents = function getBorderExtents(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // returns an object with properties useful for noting the border // dimensions. // description: // - l/t/r/b = the sum of left/top/right/bottom border (respectively) // - w = the sum of the left and right border // - h = the sum of the top and bottom border // // The w/h are used for calculating boxes. // Normally application code will not need to invoke this // directly, and will use the ...box... functions instead. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var px = style.toPixelValue, s = computedStyle || style.getComputedStyle(node), l = s.borderLeftStyle != none ? px(node, s.borderLeftWidth) : 0, t = s.borderTopStyle != none ? px(node, s.borderTopWidth) : 0, r = s.borderRightStyle != none ? px(node, s.borderRightWidth) : 0, b = s.borderBottomStyle != none ? px(node, s.borderBottomWidth) : 0; return {l: l, t: t, r: r, b: b, w: l + r, h: t + b}; }; geom.getPadBorderExtents = function getPadBorderExtents(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // Returns object with properties useful for box fitting with // regards to padding. // description: // - l/t/r/b = the sum of left/top/right/bottom padding and left/top/right/bottom border (respectively) // - w = the sum of the left and right padding and border // - h = the sum of the top and bottom padding and border // // The w/h are used for calculating boxes. // Normally application code will not need to invoke this // directly, and will use the ...box... functions instead. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), p = geom.getPadExtents(node, s), b = geom.getBorderExtents(node, s); return { l: p.l + b.l, t: p.t + b.t, r: p.r + b.r, b: p.b + b.b, w: p.w + b.w, h: p.h + b.h }; }; geom.getMarginExtents = function getMarginExtents(node, computedStyle){ // summary: // returns object with properties useful for box fitting with // regards to box margins (i.e., the outer-box). // // - l/t = marginLeft, marginTop, respectively // - w = total width, margin inclusive // - h = total height, margin inclusive // // The w/h are used for calculating boxes. // Normally application code will not need to invoke this // directly, and will use the ...box... functions instead. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue, l = px(node, s.marginLeft), t = px(node, s.marginTop), r = px(node, s.marginRight), b = px(node, s.marginBottom); return {l: l, t: t, r: r, b: b, w: l + r, h: t + b}; }; // Box getters work in any box context because offsetWidth/clientWidth // are invariant wrt box context // // They do *not* work for display: inline objects that have padding styles // because the user agent ignores padding (it's bogus styling in any case) // // Be careful with IMGs because they are inline or block depending on // browser and browser mode. // Although it would be easier to read, there are not separate versions of // _getMarginBox for each browser because: // 1. the branching is not expensive // 2. factoring the shared code wastes cycles (function call overhead) // 3. duplicating the shared code wastes bytes geom.getMarginBox = function getMarginBox(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // returns an object that encodes the width, height, left and top // positions of the node's margin box. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), me = geom.getMarginExtents(node, s), l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode, px = style.toPixelValue, pcs; if(has("mozilla")){ // Mozilla: // If offsetParent has a computed overflow != visible, the offsetLeft is decreased // by the parent's border. // We don't want to compute the parent's style, so instead we examine node's // computed left/top which is more stable. var sl = parseFloat(s.left), st = parseFloat(s.top); if(!isNaN(sl) && !isNaN(st)){ l = sl; t = st; }else{ // If child's computed left/top are not parseable as a number (e.g. "auto"), we // have no choice but to examine the parent's computed style. if(p && p.style){ pcs = style.getComputedStyle(p); if(pcs.overflow != "visible"){ l += pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0; t += pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0; } } } }else if(has("opera") || (has("ie") == 8 && !has("quirks"))){ // On Opera and IE 8, offsetLeft/Top includes the parent's border if(p){ pcs = style.getComputedStyle(p); l -= pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0; t -= pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0; } } return {l: l, t: t, w: node.offsetWidth + me.w, h: node.offsetHeight + me.h}; }; geom.getContentBox = function getContentBox(node, computedStyle){ // summary: // Returns an object that encodes the width, height, left and top // positions of the node's content box, irrespective of the // current box model. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). // clientWidth/Height are important since the automatically account for scrollbars // fallback to offsetWidth/Height for special cases (see #3378) node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), w = node.clientWidth, h, pe = geom.getPadExtents(node, s), be = geom.getBorderExtents(node, s); if(!w){ w = node.offsetWidth; h = node.offsetHeight; }else{ h = node.clientHeight; be.w = be.h = 0; } // On Opera, offsetLeft includes the parent's border if(has("opera")){ pe.l += be.l; pe.t += be.t; } return {l: pe.l, t: pe.t, w: w - pe.w - be.w, h: h - pe.h - be.h}; }; // Box setters depend on box context because interpretation of width/height styles // vary wrt box context. // // The value of boxModel is used to determine box context. // boxModel can be set directly to change behavior. // // Beware of display: inline objects that have padding styles // because the user agent ignores padding (it's a bogus setup anyway) // // Be careful with IMGs because they are inline or block depending on // browser and browser mode. // // Elements other than DIV may have special quirks, like built-in // margins or padding, or values not detectable via computedStyle. // In particular, margins on TABLE do not seems to appear // at all in computedStyle on Mozilla. function setBox(/*DomNode*/ node, /*Number?*/ l, /*Number?*/ t, /*Number?*/ w, /*Number?*/ h, /*String?*/ u){ // summary: // sets width/height/left/top in the current (native) box-model // dimensions. Uses the unit passed in u. // node: // DOM Node reference. Id string not supported for performance // reasons. // l: // left offset from parent. // t: // top offset from parent. // w: // width in current box model. // h: // width in current box model. // u: // unit measure to use for other measures. Defaults to "px". u = u || "px"; var s = node.style; if(!isNaN(l)){ s.left = l + u; } if(!isNaN(t)){ s.top = t + u; } if(w >= 0){ s.width = w + u; } if(h >= 0){ s.height = h + u; } } function isButtonTag(/*DomNode*/ node){ // summary: // True if the node is BUTTON or INPUT.type="button". return node.tagName.toLowerCase() == "button" || node.tagName.toLowerCase() == "input" && (node.getAttribute("type") || "").toLowerCase() == "button"; // boolean } function usesBorderBox(/*DomNode*/ node){ // summary: // True if the node uses border-box layout. // We could test the computed style of node to see if a particular box // has been specified, but there are details and we choose not to bother. // TABLE and BUTTON (and INPUT type=button) are always border-box by default. // If you have assigned a different box to either one via CSS then // box functions will break. return geom.boxModel == "border-box" || node.tagName.toLowerCase() == "table" || isButtonTag(node); // boolean } geom.setContentSize = function setContentSize(/*DomNode*/ node, /*Object*/ box, /*Object*/ computedStyle){ // summary: // Sets the size of the node's contents, irrespective of margins, // padding, or borders. // node: DOMNode // box: Object // hash with optional "w", and "h" properties for "width", and "height" // respectively. All specified properties should have numeric values in whole pixels. // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var w = box.w, h = box.h; if(usesBorderBox(node)){ var pb = geom.getPadBorderExtents(node, computedStyle); if(w >= 0){ w += pb.w; } if(h >= 0){ h += pb.h; } } setBox(node, NaN, NaN, w, h); }; var nilExtents = {l: 0, t: 0, w: 0, h: 0}; geom.setMarginBox = function setMarginBox(/*DomNode*/ node, /*Object*/ box, /*Object*/ computedStyle){ // summary: // sets the size of the node's margin box and placement // (left/top), irrespective of box model. Think of it as a // passthrough to setBox that handles box-model vagaries for // you. // node: DOMNode // box: Object // hash with optional "l", "t", "w", and "h" properties for "left", "right", "width", and "height" // respectively. All specified properties should have numeric values in whole pixels. // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), w = box.w, h = box.h, // Some elements have special padding, margin, and box-model settings. // To use box functions you may need to set padding, margin explicitly. // Controlling box-model is harder, in a pinch you might set dojo/dom-geometry.boxModel. pb = usesBorderBox(node) ? nilExtents : geom.getPadBorderExtents(node, s), mb = geom.getMarginExtents(node, s); if(has("webkit")){ // on Safari (3.1.2), button nodes with no explicit size have a default margin // setting an explicit size eliminates the margin. // We have to swizzle the width to get correct margin reading. if(isButtonTag(node)){ var ns = node.style; if(w >= 0 && !ns.width){ ns.width = "4px"; } if(h >= 0 && !ns.height){ ns.height = "4px"; } } } if(w >= 0){ w = Math.max(w - pb.w - mb.w, 0); } if(h >= 0){ h = Math.max(h - pb.h - mb.h, 0); } setBox(node, box.l, box.t, w, h); }; // ============================= // Positioning // ============================= geom.isBodyLtr = function isBodyLtr(/*Document?*/ doc){ // summary: // Returns true if the current language is left-to-right, and false otherwise. // doc: Document? // Optional document to query. If unspecified, use win.doc. // returns: Boolean doc = doc || win.doc; return (win.body(doc).dir || doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean }; geom.docScroll = function docScroll(/*Document?*/ doc){ // summary: // Returns an object with {node, x, y} with corresponding offsets. // doc: Document? // Optional document to query. If unspecified, use win.doc. // returns: Object doc = doc || win.doc; var node = win.doc.parentWindow || win.doc.defaultView; // use UI window, not dojo.global window. TODO: use dojo/window::get() except for circular dependency problem return "pageXOffset" in node ? {x: node.pageXOffset, y: node.pageYOffset } : (node = has("quirks") ? win.body(doc) : doc.documentElement) && {x: geom.fixIeBiDiScrollLeft(node.scrollLeft || 0, doc), y: node.scrollTop || 0 }; }; if(has("ie")){ geom.getIeDocumentElementOffset = function getIeDocumentElementOffset(/*Document?*/ doc){ // summary: // returns the offset in x and y from the document body to the // visual edge of the page for IE // doc: Document? // Optional document to query. If unspecified, use win.doc. // description: // The following values in IE contain an offset: // | event.clientX // | event.clientY // | node.getBoundingClientRect().left // | node.getBoundingClientRect().top // But other position related values do not contain this offset, // such as node.offsetLeft, node.offsetTop, node.style.left and // node.style.top. The offset is always (2, 2) in LTR direction. // When the body is in RTL direction, the offset counts the width // of left scroll bar's width. This function computes the actual // offset. //NOTE: assumes we're being called in an IE browser doc = doc || win.doc; var de = doc.documentElement; // only deal with HTML element here, position() handles body/quirks if(has("ie") < 8){ var r = de.getBoundingClientRect(), // works well for IE6+ l = r.left, t = r.top; if(has("ie") < 7){ l += de.clientLeft; // scrollbar size in strict/RTL, or, t += de.clientTop; // HTML border size in strict } return { x: l < 0 ? 0 : l, // FRAME element border size can lead to inaccurate negative values y: t < 0 ? 0 : t }; }else{ return { x: 0, y: 0 }; } }; } geom.fixIeBiDiScrollLeft = function fixIeBiDiScrollLeft(/*Integer*/ scrollLeft, /*Document?*/ doc){ // summary: // In RTL direction, scrollLeft should be a negative value, but IE // returns a positive one. All codes using documentElement.scrollLeft // must call this function to fix this error, otherwise the position // will offset to right when there is a horizontal scrollbar. // scrollLeft: Number // doc: Document? // Optional document to query. If unspecified, use win.doc. // returns: Number // In RTL direction, scrollLeft should be a negative value, but IE // returns a positive one. All codes using documentElement.scrollLeft // must call this function to fix this error, otherwise the position // will offset to right when there is a horizontal scrollbar. doc = doc || win.doc; var ie = has("ie"); if(ie && !geom.isBodyLtr(doc)){ var qk = has("quirks"), de = qk ? win.body(doc) : doc.documentElement, pwin = win.global; // TODO: use winUtils.get(doc) after resolving circular dependency b/w dom-geometry.js and dojo/window.js if(ie == 6 && !qk && pwin.frameElement && de.scrollHeight > de.clientHeight){ scrollLeft += de.clientLeft; // workaround ie6+strict+rtl+iframe+vertical-scrollbar bug where clientWidth is too small by clientLeft pixels } return (ie < 8 || qk) ? (scrollLeft + de.clientWidth - de.scrollWidth) : -scrollLeft; // Integer } return scrollLeft; // Integer }; geom.position = function(/*DomNode*/ node, /*Boolean?*/ includeScroll){ // summary: // Gets the position and size of the passed element relative to // the viewport (if includeScroll==false), or relative to the // document root (if includeScroll==true). // // description: // Returns an object of the form: // `{ x: 100, y: 300, w: 20, h: 15 }`. // If includeScroll==true, the x and y values will include any // document offsets that may affect the position relative to the // viewport. // Uses the border-box model (inclusive of border and padding but // not margin). Does not act as a setter. // node: DOMNode|String // includeScroll: Boolean? // returns: Object node = dom.byId(node); var db = win.body(node.ownerDocument), ret = node.getBoundingClientRect(); ret = {x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top}; if(has("ie") < 9){ // On IE<9 there's a 2px offset that we need to adjust for, see dojo.getIeDocumentElementOffset() var offset = geom.getIeDocumentElementOffset(node.ownerDocument); // fixes the position in IE, quirks mode ret.x -= offset.x + (has("quirks") ? db.clientLeft + db.offsetLeft : 0); ret.y -= offset.y + (has("quirks") ? db.clientTop + db.offsetTop : 0); } // account for document scrolling // if offsetParent is used, ret value already includes scroll position // so we may have to actually remove that value if !includeScroll if(includeScroll){ var scroll = geom.docScroll(node.ownerDocument); ret.x += scroll.x; ret.y += scroll.y; } return ret; // Object }; // random "private" functions wildly used throughout the toolkit geom.getMarginSize = function getMarginSize(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // returns an object that encodes the width and height of // the node's margin box // node: DOMNode|String // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo/dom-style.getComputedStyle to get one. It is a better way, calling // dojo/dom-style.getComputedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var me = geom.getMarginExtents(node, computedStyle || style.getComputedStyle(node)); var size = node.getBoundingClientRect(); return { w: (size.right - size.left) + me.w, h: (size.bottom - size.top) + me.h }; }; geom.normalizeEvent = function(event){ // summary: // Normalizes the geometry of a DOM event, normalizing the pageX, pageY, // offsetX, offsetY, layerX, and layerX properties // event: Object if(!("layerX" in event)){ event.layerX = event.offsetX; event.layerY = event.offsetY; } if(!has("dom-addeventlistener")){ // old IE version // FIXME: scroll position query is duped from dojo/_base/html to // avoid dependency on that entire module. Now that HTML is in // Base, we should convert back to something similar there. var se = event.target; var doc = (se && se.ownerDocument) || document; // DO NOT replace the following to use dojo/_base/window.body(), in IE, document.documentElement should be used // here rather than document.body var docBody = has("quirks") ? doc.body : doc.documentElement; var offset = geom.getIeDocumentElementOffset(doc); event.pageX = event.clientX + geom.fixIeBiDiScrollLeft(docBody.scrollLeft || 0, doc) - offset.x; event.pageY = event.clientY + (docBody.scrollTop || 0) - offset.y; } }; // TODO: evaluate separate getters/setters for position and sizes? return geom; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | 1 | define(["exports", "./_base/kernel", "./sniff", "./_base/lang", "./dom", "./dom-style", "./dom-construct", "./_base/connect"], function(exports, dojo, has, lang, dom, style, ctr, conn){ // module: // dojo/dom-prop // summary: // This module defines the core dojo DOM properties API. // TODOC: summary not showing up in output, see https://github.com/csnover/js-doc-parse/issues/42 // ============================= // Element properties Functions // ============================= // helper to connect events var _evtHdlrMap = {}, _ctr = 0, _attrId = dojo._scopeName + "attrid"; exports.names = { // properties renamed to avoid clashes with reserved words "class": "className", "for": "htmlFor", // properties written as camelCase tabindex: "tabIndex", readonly: "readOnly", colspan: "colSpan", frameborder: "frameBorder", rowspan: "rowSpan", valuetype: "valueType" }; exports.get = function getProp(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Gets a property on an HTML element. // description: // Handles normalized getting of properties on DOM nodes. // // node: DOMNode|String // id or reference to the element to get the property on // name: String // the name of the property to get. // returns: // the value of the requested property or its default value // // example: // | // get the current value of the "foo" property on a node // | require(["dojo/dom-prop", "dojo/dom"], function(domProp, dom){ // | domProp.get(dom.byId("nodeId"), "foo"); // | // or we can just pass the id: // | domProp.get("nodeId", "foo"); // | }); node = dom.byId(node); var lc = name.toLowerCase(), propName = exports.names[lc] || name; return node[propName]; // Anything }; exports.set = function setProp(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){ // summary: // Sets a property on an HTML element. // description: // Handles normalized setting of properties on DOM nodes. // // When passing functions as values, note that they will not be // directly assigned to slots on the node, but rather the default // behavior will be removed and the new behavior will be added // using `dojo.connect()`, meaning that event handler properties // will be normalized and that some caveats with regards to // non-standard behaviors for onsubmit apply. Namely that you // should cancel form submission using `dojo.stopEvent()` on the // passed event object instead of returning a boolean value from // the handler itself. // node: DOMNode|String // id or reference to the element to set the property on // name: String|Object // the name of the property to set, or a hash object to set // multiple properties at once. // value: String? // The value to set for the property // returns: // the DOM node // // example: // | // use prop() to set the tab index // | require(["dojo/dom-prop"], function(domProp){ // | domProp.set("nodeId", "tabIndex", 3); // | }); // // example: // Set multiple values at once, including event handlers: // | require(["dojo/dom-prop"], function(domProp){ // | domProp.set("formId", { // | "foo": "bar", // | "tabIndex": -1, // | "method": "POST", // | }); // | }); node = dom.byId(node); var l = arguments.length; if(l == 2 && typeof name != "string"){ // inline'd type check // the object form of setter: the 2nd argument is a dictionary for(var x in name){ exports.set(node, x, name[x]); } return node; // DomNode } var lc = name.toLowerCase(), propName = exports.names[lc] || name; if(propName == "style" && typeof value != "string"){ // inline'd type check // special case: setting a style style.set(node, value); return node; // DomNode } if(propName == "innerHTML"){ // special case: assigning HTML // the hash lists elements with read-only innerHTML on IE if(has("ie") && node.tagName.toLowerCase() in {col: 1, colgroup: 1, table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1}){ ctr.empty(node); node.appendChild(ctr.toDom(value, node.ownerDocument)); }else{ node[propName] = value; } return node; // DomNode } if(lang.isFunction(value)){ // special case: assigning an event handler // clobber if we can var attrId = node[_attrId]; if(!attrId){ attrId = _ctr++; node[_attrId] = attrId; } if(!_evtHdlrMap[attrId]){ _evtHdlrMap[attrId] = {}; } var h = _evtHdlrMap[attrId][propName]; if(h){ //h.remove(); conn.disconnect(h); }else{ try{ delete node[propName]; }catch(e){} } // ensure that event objects are normalized, etc. if(value){ //_evtHdlrMap[attrId][propName] = on(node, propName, value); _evtHdlrMap[attrId][propName] = conn.connect(node, propName, value); }else{ node[propName] = null; } return node; // DomNode } node[propName] = value; return node; // DomNode }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | 1 1 | define(["./sniff", "./dom"], function(has, dom){ // module: // dojo/dom-style // ============================= // Style Functions // ============================= // getComputedStyle drives most of the style code. // Wherever possible, reuse the returned object. // // API functions below that need to access computed styles accept an // optional computedStyle parameter. // If this parameter is omitted, the functions will call getComputedStyle themselves. // This way, calling code can access computedStyle once, and then pass the reference to // multiple API functions. // Although we normally eschew argument validation at this // level, here we test argument 'node' for (duck)type, // by testing nodeType, ecause 'document' is the 'parentNode' of 'body' // it is frequently sent to this function even // though it is not Element. var getComputedStyle, style = { // summary: // This module defines the core dojo DOM style API. }; if(has("webkit")){ getComputedStyle = function(/*DomNode*/ node){ var s; if(node.nodeType == 1){ var dv = node.ownerDocument.defaultView; s = dv.getComputedStyle(node, null); if(!s && node.style){ node.style.display = ""; s = dv.getComputedStyle(node, null); } } return s || {}; }; }else if(has("ie") && (has("ie") < 9 || has("quirks"))){ getComputedStyle = function(node){ // IE (as of 7) doesn't expose Element like sane browsers // currentStyle can be null on IE8! return node.nodeType == 1 /* ELEMENT_NODE*/ && node.currentStyle ? node.currentStyle : {}; }; }else{ getComputedStyle = function(node){ return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.ownerDocument.defaultView.getComputedStyle(node, null) : {}; }; } style.getComputedStyle = getComputedStyle; /*===== style.getComputedStyle = function(node){ // summary: // Returns a "computed style" object. // // description: // Gets a "computed style" object which can be used to gather // information about the current state of the rendered node. // // Note that this may behave differently on different browsers. // Values may have different formats and value encodings across // browsers. // // Note also that this method is expensive. Wherever possible, // reuse the returned object. // // Use the dojo/dom-style.get() method for more consistent (pixelized) // return values. // // node: DOMNode // A reference to a DOM node. Does NOT support taking an // ID string for speed reasons. // example: // | require(["dojo/dom-style", "dojo/dom"], function(domStyle, dom){ // | domStyle.getComputedStyle(dom.byId('foo')).borderWidth; // | }); // // example: // Reusing the returned object, avoiding multiple lookups: // | require(["dojo/dom-style", "dojo/dom"], function(domStyle, dom){ // | var cs = domStyle.getComputedStyle(dom.byId("someNode")); // | var w = cs.width, h = cs.height; // | }); return; // CSS2Properties }; =====*/ var toPixel; if(!has("ie")){ toPixel = function(element, value){ // style values can be floats, client code may want // to round for integer pixels. return parseFloat(value) || 0; }; }else{ toPixel = function(element, avalue){ if(!avalue){ return 0; } // on IE7, medium is usually 4 pixels if(avalue == "medium"){ return 4; } // style values can be floats, client code may // want to round this value for integer pixels. if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); } var s = element.style, rs = element.runtimeStyle, cs = element.currentStyle, sLeft = s.left, rsLeft = rs.left; rs.left = cs.left; try{ // 'avalue' may be incompatible with style.left, which can cause IE to throw // this has been observed for border widths using "thin", "medium", "thick" constants // those particular constants could be trapped by a lookup // but perhaps there are more s.left = avalue; avalue = s.pixelLeft; }catch(e){ avalue = 0; } s.left = sLeft; rs.left = rsLeft; return avalue; }; } style.toPixelValue = toPixel; /*===== style.toPixelValue = function(node, value){ // summary: // converts style value to pixels on IE or return a numeric value. // node: DOMNode // value: String // returns: Number }; =====*/ // FIXME: there opacity quirks on FF that we haven't ported over. Hrm. var astr = "DXImageTransform.Microsoft.Alpha"; var af = function(n, f){ try{ return n.filters.item(astr); }catch(e){ return f ? {} : null; } }; var _getOpacity = has("ie") < 9 || (has("ie") < 10 && has("quirks")) ? function(node){ try{ return af(node).Opacity / 100; // Number }catch(e){ return 1; // Number } } : function(node){ return getComputedStyle(node).opacity; }; var _setOpacity = has("ie") < 9 || (has("ie") < 10 && has("quirks")) ? function(/*DomNode*/ node, /*Number*/ opacity){ if(opacity === ""){ opacity = 1; } var ov = opacity * 100, fullyOpaque = opacity === 1; // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661), // but still update the opacity value so we can get a correct reading if it is read later: // af(node, 1).Enabled = !fullyOpaque; if(fullyOpaque){ node.style.zoom = ""; if(af(node)){ node.style.filter = node.style.filter.replace( new RegExp("\\s*progid:" + astr + "\\([^\\)]+?\\)", "i"), ""); } }else{ node.style.zoom = 1; if(af(node)){ af(node, 1).Opacity = ov; }else{ node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; } af(node, 1).Enabled = true; } if(node.tagName.toLowerCase() == "tr"){ for(var td = node.firstChild; td; td = td.nextSibling){ if(td.tagName.toLowerCase() == "td"){ _setOpacity(td, opacity); } } } return opacity; } : function(node, opacity){ return node.style.opacity = opacity; }; var _pixelNamesCache = { left: true, top: true }; var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border function _toStyleValue(node, type, value){ //TODO: should we really be doing string case conversion here? Should we cache it? Need to profile! type = type.toLowerCase(); if(has("ie")){ if(value == "auto"){ if(type == "height"){ return node.offsetHeight; } if(type == "width"){ return node.offsetWidth; } } if(type == "fontweight"){ switch(value){ case 700: return "bold"; case 400: default: return "normal"; } } } if(!(type in _pixelNamesCache)){ _pixelNamesCache[type] = _pixelRegExp.test(type); } return _pixelNamesCache[type] ? toPixel(node, value) : value; } var _floatAliases = {cssFloat: 1, styleFloat: 1, "float": 1}; // public API style.get = function getStyle(/*DOMNode|String*/ node, /*String?*/ name){ // summary: // Accesses styles on a node. // description: // Getting the style value uses the computed style for the node, so the value // will be a calculated value, not just the immediate node.style value. // Also when getting values, use specific style names, // like "borderBottomWidth" instead of "border" since compound values like // "border" are not necessarily reflected as expected. // If you want to get node dimensions, use `dojo/dom-geometry.getMarginBox()`, // `dojo/dom-geometry.getContentBox()` or `dojo/dom-geometry.getPosition()`. // node: DOMNode|String // id or reference to node to get style for // name: String? // the style property to get // example: // Passing only an ID or node returns the computed style object of // the node: // | require(["dojo/dom-style", "dojo/dom"], function(domStyle, dom){ // | domStyle.get("thinger"); // | }); // example: // Passing a node and a style property returns the current // normalized, computed value for that property: // | require(["dojo/dom-style", "dojo/dom"], function(domStyle, dom){ // | domStyle.get("thinger", "opacity"); // 1 by default // | }); var n = dom.byId(node), l = arguments.length, op = (name == "opacity"); if(l == 2 && op){ return _getOpacity(n); } name = _floatAliases[name] ? "cssFloat" in n.style ? "cssFloat" : "styleFloat" : name; var s = style.getComputedStyle(n); return (l == 1) ? s : _toStyleValue(n, name, s[name] || n.style[name]); /* CSS2Properties||String||Number */ }; style.set = function setStyle(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){ // summary: // Sets styles on a node. // node: DOMNode|String // id or reference to node to set style for // name: String|Object // the style property to set in DOM-accessor format // ("borderWidth", not "border-width") or an object with key/value // pairs suitable for setting each property. // value: String? // If passed, sets value on the node for style, handling // cross-browser concerns. When setting a pixel value, // be sure to include "px" in the value. For instance, top: "200px". // Otherwise, in some cases, some browsers will not apply the style. // // example: // Passing a node, a style property, and a value changes the // current display of the node and returns the new computed value // | require(["dojo/dom-style"], function(domStyle){ // | domStyle.set("thinger", "opacity", 0.5); // == 0.5 // | }); // // example: // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node: // | require(["dojo/dom-style"], function(domStyle){ // | domStyle.set("thinger", { // | "opacity": 0.5, // | "border": "3px solid black", // | "height": "300px" // | }); // | }); // // example: // When the CSS style property is hyphenated, the JavaScript property is camelCased. // font-size becomes fontSize, and so on. // | require(["dojo/dom-style", "dojo/dom"], function(domStyle, dom){ // | domStyle.set("thinger",{ // | fontSize:"14pt", // | letterSpacing:"1.2em" // | }); // | }); // // example: // dojo/NodeList implements .style() using the same syntax, omitting the "node" parameter, calling // dojo/dom-style.get() on every element of the list. See: `dojo/query` and `dojo/NodeList` // | require(["dojo/dom-style", "dojo/query", "dojo/NodeList-dom"], // | function(domStyle, query){ // | query(".someClassName").style("visibility","hidden"); // | // or // | query("#baz > div").style({ // | opacity:0.75, // | fontSize:"13pt" // | }); // | }); var n = dom.byId(node), l = arguments.length, op = (name == "opacity"); name = _floatAliases[name] ? "cssFloat" in n.style ? "cssFloat" : "styleFloat" : name; if(l == 3){ return op ? _setOpacity(n, value) : n.style[name] = value; // Number } for(var x in name){ style.set(node, x, name[x]); } return style.getComputedStyle(n); }; return style; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | 1 | define(["./sniff", "./_base/window"], function(has, win){ // module: // dojo/dom // FIXME: need to add unit tests for all the semi-public methods if(has("ie") <= 7){ try{ document.execCommand("BackgroundImageCache", false, true); }catch(e){ // sane browsers don't have cache "issues" } } // ============================= // DOM Functions // ============================= // the result object var dom = { // summary: // This module defines the core dojo DOM API. }; if(has("ie")){ dom.byId = function(id, doc){ if(typeof id != "string"){ return id; } var _d = doc || win.doc, te = id && _d.getElementById(id); // attributes.id.value is better than just id in case the // user has a name=id inside a form if(te && (te.attributes.id.value == id || te.id == id)){ return te; }else{ var eles = _d.all[id]; if(!eles || eles.nodeName){ eles = [eles]; } // if more than 1, choose first with the correct id var i = 0; while((te = eles[i++])){ if((te.attributes && te.attributes.id && te.attributes.id.value == id) || te.id == id){ return te; } } } }; }else{ dom.byId = function(id, doc){ // inline'd type check. // be sure to return null per documentation, to match IE branch. return ((typeof id == "string") ? (doc || win.doc).getElementById(id) : id) || null; // DOMNode }; } /*===== dom.byId = function(id, doc){ // summary: // Returns DOM node with matching `id` attribute or falsy value (ex: null or undefined) // if not found. If `id` is a DomNode, this function is a no-op. // // id: String|DOMNode // A string to match an HTML id attribute or a reference to a DOM Node // // doc: Document? // Document to work in. Defaults to the current value of // dojo/_base/window.doc. Can be used to retrieve // node references from other documents. // // example: // Look up a node by ID: // | require(["dojo/dom"], function(dom){ // | var n = dom.byId("foo"); // | }); // // example: // Check if a node exists, and use it. // | require(["dojo/dom"], function(dom){ // | var n = dom.byId("bar"); // | if(n){ doStuff() ... } // | }); // // example: // Allow string or DomNode references to be passed to a custom function: // | require(["dojo/dom"], function(dom){ // | var foo = function(nodeOrId){ // | nodeOrId = dom.byId(nodeOrId); // | // ... more stuff // | } // | }); }; =====*/ dom.isDescendant = function(/*DOMNode|String*/ node, /*DOMNode|String*/ ancestor){ // summary: // Returns true if node is a descendant of ancestor // node: DOMNode|String // string id or node reference to test // ancestor: DOMNode|String // string id or node reference of potential parent to test against // // example: // Test is node id="bar" is a descendant of node id="foo" // | require(["dojo/dom"], function(dom){ // | if(dom.isDescendant("bar", "foo")){ ... } // | }); try{ node = dom.byId(node); ancestor = dom.byId(ancestor); while(node){ if(node == ancestor){ return true; // Boolean } node = node.parentNode; } }catch(e){ /* squelch, return false */ } return false; // Boolean }; // TODO: do we need setSelectable in the base? // Add feature test for user-select CSS property // (currently known to work in all but IE < 10 and Opera) has.add("css-user-select", function(global, doc, element){ // Avoid exception when dom.js is loaded in non-browser environments if(!element){ return false; } var style = element.style; var prefixes = ["Khtml", "O", "ms", "Moz", "Webkit"], i = prefixes.length, name = "userSelect", prefix; // Iterate prefixes from most to least likely do{ if(typeof style[name] !== "undefined"){ // Supported; return property name return name; } }while(i-- && (name = prefixes[i] + "UserSelect")); // Not supported if we didn't return before now return false; }); /*===== dom.setSelectable = function(node, selectable){ // summary: // Enable or disable selection on a node // node: DOMNode|String // id or reference to node // selectable: Boolean // state to put the node in. false indicates unselectable, true // allows selection. // example: // Make the node id="bar" unselectable // | require(["dojo/dom"], function(dom){ // | dom.setSelectable("bar"); // | }); // example: // Make the node id="bar" selectable // | require(["dojo/dom"], function(dom){ // | dom.setSelectable("bar", true); // | }); }; =====*/ var cssUserSelect = has("css-user-select"); dom.setSelectable = cssUserSelect ? function(node, selectable){ // css-user-select returns a (possibly vendor-prefixed) CSS property name dom.byId(node).style[cssUserSelect] = selectable ? "" : "none"; } : function(node, selectable){ node = dom.byId(node); // (IE < 10 / Opera) Fall back to setting/removing the // unselectable attribute on the element and all its children var nodes = node.getElementsByTagName("*"), i = nodes.length; if(selectable){ node.removeAttribute("unselectable"); while(i--){ nodes[i].removeAttribute("unselectable"); } }else{ node.setAttribute("unselectable", "on"); while(i--){ nodes[i].setAttribute("unselectable", "on"); } } }; return dom; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 1 1 1 | define(['./has'], function(has){ var global = this, doc = document, readyStates = { 'loaded': 1, 'complete': 1 }, fixReadyState = typeof doc.readyState != "string", ready = !!readyStates[doc.readyState], readyQ = [], recursiveGuard; function domReady(callback){ // summary: // Plugin to delay require()/define() callback from firing until the DOM has finished loading. readyQ.push(callback); if(ready){ processQ(); } } domReady.load = function(id, req, load){ domReady(load); }; // Export queue so that ready() can check if it's empty or not. domReady._Q = readyQ; domReady._onQEmpty = function(){ // summary: // Private method overridden by dojo/ready, to notify when everything in the // domReady queue has been processed. Do not use directly. // Will be removed in 2.0, along with domReady._Q. }; // For FF <= 3.5 if(fixReadyState){ doc.readyState = "loading"; } function processQ(){ // Calls all functions in the queue in order, unless processQ() is already running, in which case just return if(recursiveGuard){ return; } recursiveGuard = true; while(readyQ.length){ try{ (readyQ.shift())(doc); }catch(err){ console.log("Error on domReady callback: " + err); } } recursiveGuard = false; // Notification for dojo/ready. Remove for 2.0. // Note that this could add more tasks to the ready queue. domReady._onQEmpty(); } if(!ready){ var tests = [], detectReady = function(evt){ evt = evt || global.event; if(ready || (evt.type == "readystatechange" && !readyStates[doc.readyState])){ return; } // For FF <= 3.5 if(fixReadyState){ doc.readyState = "complete"; } ready = 1; processQ(); }, on = function(node, event){ node.addEventListener(event, detectReady, false); readyQ.push(function(){ node.removeEventListener(event, detectReady, false); }); }; if(!has("dom-addeventlistener")){ on = function(node, event){ event = "on" + event; node.attachEvent(event, detectReady); readyQ.push(function(){ node.detachEvent(event, detectReady); }); }; var div = doc.createElement("div"); try{ if(div.doScroll && global.frameElement === null){ // the doScroll test is only useful if we're in the top-most frame tests.push(function(){ // Derived with permission from Diego Perini's IEContentLoaded // http://javascript.nwbox.com/IEContentLoaded/ try{ div.doScroll("left"); return 1; }catch(e){} }); } }catch(e){} } on(doc, "DOMContentLoaded"); on(global, "load"); if("onreadystatechange" in doc){ on(doc, "readystatechange"); }else if(!fixReadyState){ // if the ready state property exists and there's // no readystatechange event, poll for the state // to change tests.push(function(){ return readyStates[doc.readyState]; }); } if(tests.length){ var poller = function(){ if(ready){ return; } var i = tests.length; while(i--){ if(tests[i]()){ detectReady("poller"); return; } } setTimeout(poller, 30); }; poller(); } } return domReady; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | 1 | define([ "./_base/lang", "./Evented", "./_base/kernel", "./_base/array", "./aspect", "./_base/fx", "./dom", "./dom-style", "./dom-geometry", "./ready", "require" // for context sensitive loading of Toggler ], function(lang, Evented, dojo, arrayUtil, aspect, baseFx, dom, domStyle, geom, ready, require){ // module: // dojo/fx // For back-compat, remove in 2.0. if(!dojo.isAsync){ ready(0, function(){ var requires = ["./fx/Toggler"]; require(requires); // use indirection so modules not rolled into a build }); } var coreFx = dojo.fx = { // summary: // Effects library on top of Base animations }; var _baseObj = { _fire: function(evt, args){ if(this[evt]){ this[evt].apply(this, args||[]); } return this; } }; var _chain = function(animations){ this._index = -1; this._animations = animations||[]; this._current = this._onAnimateCtx = this._onEndCtx = null; this.duration = 0; arrayUtil.forEach(this._animations, function(a){ this.duration += a.duration; if(a.delay){ this.duration += a.delay; } }, this); }; _chain.prototype = new Evented(); lang.extend(_chain, { _onAnimate: function(){ this._fire("onAnimate", arguments); }, _onEnd: function(){ this._onAnimateCtx.remove(); this._onEndCtx.remove(); this._onAnimateCtx = this._onEndCtx = null; if(this._index + 1 == this._animations.length){ this._fire("onEnd"); }else{ // switch animations this._current = this._animations[++this._index]; this._onAnimateCtx = aspect.after(this._current, "onAnimate", lang.hitch(this, "_onAnimate"), true); this._onEndCtx = aspect.after(this._current, "onEnd", lang.hitch(this, "_onEnd"), true); this._current.play(0, true); } }, play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ if(!this._current){ this._current = this._animations[this._index = 0]; } if(!gotoStart && this._current.status() == "playing"){ return this; } var beforeBegin = aspect.after(this._current, "beforeBegin", lang.hitch(this, function(){ this._fire("beforeBegin"); }), true), onBegin = aspect.after(this._current, "onBegin", lang.hitch(this, function(arg){ this._fire("onBegin", arguments); }), true), onPlay = aspect.after(this._current, "onPlay", lang.hitch(this, function(arg){ this._fire("onPlay", arguments); beforeBegin.remove(); onBegin.remove(); onPlay.remove(); })); if(this._onAnimateCtx){ this._onAnimateCtx.remove(); } this._onAnimateCtx = aspect.after(this._current, "onAnimate", lang.hitch(this, "_onAnimate"), true); if(this._onEndCtx){ this._onEndCtx.remove(); } this._onEndCtx = aspect.after(this._current, "onEnd", lang.hitch(this, "_onEnd"), true); this._current.play.apply(this._current, arguments); return this; }, pause: function(){ if(this._current){ var e = aspect.after(this._current, "onPause", lang.hitch(this, function(arg){ this._fire("onPause", arguments); e.remove(); }), true); this._current.pause(); } return this; }, gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ this.pause(); var offset = this.duration * percent; this._current = null; arrayUtil.some(this._animations, function(a){ if(a.duration <= offset){ this._current = a; return true; } offset -= a.duration; return false; }); if(this._current){ this._current.gotoPercent(offset / this._current.duration, andPlay); } return this; }, stop: function(/*boolean?*/ gotoEnd){ if(this._current){ if(gotoEnd){ for(; this._index + 1 < this._animations.length; ++this._index){ this._animations[this._index].stop(true); } this._current = this._animations[this._index]; } var e = aspect.after(this._current, "onStop", lang.hitch(this, function(arg){ this._fire("onStop", arguments); e.remove(); }), true); this._current.stop(); } return this; }, status: function(){ return this._current ? this._current.status() : "stopped"; }, destroy: function(){ if(this._onAnimateCtx){ this._onAnimateCtx.remove(); } if(this._onEndCtx){ this._onEndCtx.remove(); } } }); lang.extend(_chain, _baseObj); coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){ // summary: // Chain a list of `dojo/_base/fx.Animation`s to run in sequence // // description: // Return a `dojo/_base/fx.Animation` which will play all passed // `dojo/_base/fx.Animation` instances in sequence, firing its own // synthesized events simulating a single animation. (eg: // onEnd of this animation means the end of the chain, // not the individual animations within) // // example: // Once `node` is faded out, fade in `otherNode` // | require(["dojo/fx"], function(fx){ // | fx.chain([ // | fx.fadeIn({ node:node }), // | fx.fadeOut({ node:otherNode }) // | ]).play(); // | }); // return new _chain(animations); // dojo/_base/fx.Animation }; var _combine = function(animations){ this._animations = animations||[]; this._connects = []; this._finished = 0; this.duration = 0; arrayUtil.forEach(animations, function(a){ var duration = a.duration; if(a.delay){ duration += a.delay; } if(this.duration < duration){ this.duration = duration; } this._connects.push(aspect.after(a, "onEnd", lang.hitch(this, "_onEnd"), true)); }, this); this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration}); var self = this; arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"], function(evt){ self._connects.push(aspect.after(self._pseudoAnimation, evt, function(){ self._fire(evt, arguments); }, true)); } ); }; lang.extend(_combine, { _doAction: function(action, args){ arrayUtil.forEach(this._animations, function(a){ a[action].apply(a, args); }); return this; }, _onEnd: function(){ if(++this._finished > this._animations.length){ this._fire("onEnd"); } }, _call: function(action, args){ var t = this._pseudoAnimation; t[action].apply(t, args); }, play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ this._finished = 0; this._doAction("play", arguments); this._call("play", arguments); return this; }, pause: function(){ this._doAction("pause", arguments); this._call("pause", arguments); return this; }, gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){ var ms = this.duration * percent; arrayUtil.forEach(this._animations, function(a){ a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay); }); this._call("gotoPercent", arguments); return this; }, stop: function(/*boolean?*/ gotoEnd){ this._doAction("stop", arguments); this._call("stop", arguments); return this; }, status: function(){ return this._pseudoAnimation.status(); }, destroy: function(){ arrayUtil.forEach(this._connects, function(handle){ handle.remove(); }); } }); lang.extend(_combine, _baseObj); coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){ // summary: // Combine a list of `dojo/_base/fx.Animation`s to run in parallel // // description: // Combine an array of `dojo/_base/fx.Animation`s to run in parallel, // providing a new `dojo/_base/fx.Animation` instance encompasing each // animation, firing standard animation events. // // example: // Fade out `node` while fading in `otherNode` simultaneously // | require(["dojo/fx"], function(fx){ // | fx.combine([ // | fx.fadeIn({ node:node }), // | fx.fadeOut({ node:otherNode }) // | ]).play(); // | }); // // example: // When the longest animation ends, execute a function: // | require(["dojo/fx"], function(fx){ // | var anim = fx.combine([ // | fx.fadeIn({ node: n, duration:700 }), // | fx.fadeOut({ node: otherNode, duration: 300 }) // | ]); // | aspect.after(anim, "onEnd", function(){ // | // overall animation is done. // | }, true); // | anim.play(); // play the animation // | }); // return new _combine(animations); // dojo/_base/fx.Animation }; coreFx.wipeIn = function(/*Object*/ args){ // summary: // Expand a node to it's natural height. // // description: // Returns an animation that will expand the // node defined in 'args' object from it's current height to // it's natural height (with no scrollbar). // Node must have no margin/border/padding. // // args: Object // A hash-map of standard `dojo/_base/fx.Animation` constructor properties // (such as easing: node: duration: and so on) // // example: // | require(["dojo/fx"], function(fx){ // | fx.wipeIn({ // | node:"someId" // | }).play() // | }); var node = args.node = dom.byId(args.node), s = node.style, o; var anim = baseFx.animateProperty(lang.mixin({ properties: { height: { // wrapped in functions so we wait till the last second to query (in case value has changed) start: function(){ // start at current [computed] height, but use 1px rather than 0 // because 0 causes IE to display the whole panel o = s.overflow; s.overflow = "hidden"; if(s.visibility == "hidden" || s.display == "none"){ s.height = "1px"; s.display = ""; s.visibility = ""; return 1; }else{ var height = domStyle.get(node, "height"); return Math.max(height, 1); } }, end: function(){ return node.scrollHeight; } } } }, args)); var fini = function(){ s.height = "auto"; s.overflow = o; }; aspect.after(anim, "onStop", fini, true); aspect.after(anim, "onEnd", fini, true); return anim; // dojo/_base/fx.Animation }; coreFx.wipeOut = function(/*Object*/ args){ // summary: // Shrink a node to nothing and hide it. // // description: // Returns an animation that will shrink node defined in "args" // from it's current height to 1px, and then hide it. // // args: Object // A hash-map of standard `dojo/_base/fx.Animation` constructor properties // (such as easing: node: duration: and so on) // // example: // | require(["dojo/fx"], function(fx){ // | fx.wipeOut({ node:"someId" }).play() // | }); var node = args.node = dom.byId(args.node), s = node.style, o; var anim = baseFx.animateProperty(lang.mixin({ properties: { height: { end: 1 // 0 causes IE to display the whole panel } } }, args)); aspect.after(anim, "beforeBegin", function(){ o = s.overflow; s.overflow = "hidden"; s.display = ""; }, true); var fini = function(){ s.overflow = o; s.height = "auto"; s.display = "none"; }; aspect.after(anim, "onStop", fini, true); aspect.after(anim, "onEnd", fini, true); return anim; // dojo/_base/fx.Animation }; coreFx.slideTo = function(/*Object*/ args){ // summary: // Slide a node to a new top/left position // // description: // Returns an animation that will slide "node" // defined in args Object from its current position to // the position defined by (args.left, args.top). // // args: Object // A hash-map of standard `dojo/_base/fx.Animation` constructor properties // (such as easing: node: duration: and so on). Special args members // are `top` and `left`, which indicate the new position to slide to. // // example: // | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play() var node = args.node = dom.byId(args.node), top = null, left = null; var init = (function(n){ return function(){ var cs = domStyle.getComputedStyle(n); var pos = cs.position; top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0); left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0); if(pos != 'absolute' && pos != 'relative'){ var ret = geom.position(n, true); top = ret.y; left = ret.x; n.style.position="absolute"; n.style.top=top+"px"; n.style.left=left+"px"; } }; })(node); init(); var anim = baseFx.animateProperty(lang.mixin({ properties: { top: args.top || 0, left: args.left || 0 } }, args)); aspect.after(anim, "beforeBegin", init, true); return anim; // dojo/_base/fx.Animation }; return coreFx; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 1 | define(["./_base/lang", "./sniff"], function(lang, has){ // module: // dojo/gears var gears = { // summary: // TODOC }; lang.setObject("dojo.gears", gears); gears._gearsObject = function(){ // summary: // factory method to get a Google Gears plugin instance to // expose in the browser runtime environment, if present var factory; var gearsObj = lang.getObject("google.gears"); if(gearsObj){ return gearsObj; } // already defined elsewhere if(typeof GearsFactory != "undefined"){ // Firefox factory = new GearsFactory(); }else{ if(has("ie")){ // IE try{ factory = new ActiveXObject("Gears.Factory"); }catch(e){ // ok to squelch; there's no gears factory. move on. } }else if(navigator.mimeTypes["application/x-googlegears"]){ // Safari? factory = document.createElement("object"); factory.setAttribute("type", "application/x-googlegears"); factory.setAttribute("width", 0); factory.setAttribute("height", 0); factory.style.display = "none"; document.documentElement.appendChild(factory); } } // still nothing? if(!factory){ return null; } // define the global objects now; don't overwrite them though if they // were somehow set internally by the Gears plugin, which is on their // dev roadmap for the future lang.setObject("google.gears.factory", factory); return lang.getObject("google.gears"); }; // see if we have Google Gears installed, and if // so, make it available in the runtime environment // and in the Google standard 'google.gears' global object gears.available = (!!gears._gearsObject())||0; /*===== gears.available = { // summary: // True if client is using Google Gears }; =====*/ return gears; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | 1 | define(["require", "module"], function(require, module){ // module: // dojo/has // summary: // Defines the has.js API and several feature tests used by dojo. // description: // This module defines the has API as described by the project has.js with the following additional features: // // - the has test cache is exposed at has.cache. // - the method has.add includes a forth parameter that controls whether or not existing tests are replaced // - the loader's has cache may be optionally copied into this module's has cahce. // // This module adopted from https://github.com/phiggins42/has.js; thanks has.js team! // try to pull the has implementation from the loader; both the dojo loader and bdLoad provide one // if using a foreign loader, then the has cache may be initialized via the config object for this module // WARNING: if a foreign loader defines require.has to be something other than the has.js API, then this implementation fail var has = require.has || function(){}; if(!has("dojo-has-api")){ var isBrowser = // the most fundamental decision: are we in the browser? typeof window != "undefined" && typeof location != "undefined" && typeof document != "undefined" && window.location == location && window.document == document, // has API variables global = this, doc = isBrowser && document, element = doc && doc.createElement("DiV"), cache = (module.config && module.config()) || {}; has = function(name){ // summary: // Return the current value of the named feature. // // name: String|Integer // The name (if a string) or identifier (if an integer) of the feature to test. // // description: // Returns the value of the feature named by name. The feature must have been // previously added to the cache by has.add. return typeof cache[name] == "function" ? (cache[name] = cache[name](global, doc, element)) : cache[name]; // Boolean }; has.cache = cache; has.add = function(name, test, now, force){ // summary: // Register a new feature test for some named feature. // name: String|Integer // The name (if a string) or identifier (if an integer) of the feature to test. // test: Function // A test function to register. If a function, queued for testing until actually // needed. The test function should return a boolean indicating // the presence of a feature or bug. // now: Boolean? // Optional. Omit if `test` is not a function. Provides a way to immediately // run the test and cache the result. // force: Boolean? // Optional. If the test already exists and force is truthy, then the existing // test will be replaced; otherwise, add does not replace an existing test (that // is, by default, the first test advice wins). // example: // A redundant test, testFn with immediate execution: // | has.add("javascript", function(){ return true; }, true); // // example: // Again with the redundantness. You can do this in your tests, but we should // not be doing this in any internal has.js tests // | has.add("javascript", true); // // example: // Three things are passed to the testFunction. `global`, `document`, and a generic element // from which to work your test should the need arise. // | has.add("bug-byid", function(g, d, el){ // | // g == global, typically window, yadda yadda // | // d == document object // | // el == the generic element. a `has` element. // | return false; // fake test, byid-when-form-has-name-matching-an-id is slightly longer // | }); (typeof cache[name]=="undefined" || force) && (cache[name]= test); return now && has(name); }; // since we're operating under a loader that doesn't provide a has API, we must explicitly initialize // has as it would have otherwise been initialized by the dojo loader; use has.add to the builder // can optimize these away iff desired has.add("host-browser", isBrowser); has.add("host-node", (typeof process == "object" && process.versions && process.versions.node && process.versions.v8)); has.add("host-rhino", (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"))); has.add("dom", isBrowser); has.add("dojo-dom-ready-api", 1); has.add("dojo-sniff", 1); } if(has("host-browser")){ // Common application level tests has.add("dom-addeventlistener", !!document.addEventListener); has.add("touch", "ontouchstart" in document || window.navigator.msMaxTouchPoints > 0); // I don't know if any of these tests are really correct, just a rough guess has.add("device-width", screen.availWidth || innerWidth); // Tests for DOMNode.attributes[] behavior: // - dom-attributes-explicit - attributes[] only lists explicitly user specified attributes // - dom-attributes-specified-flag (IE8) - need to check attr.specified flag to skip attributes user didn't specify // - Otherwise, in IE6-7. attributes[] will list hundreds of values, so need to do outerHTML to get attrs instead. var form = document.createElement("form"); has.add("dom-attributes-explicit", form.attributes.length == 0); // W3C has.add("dom-attributes-specified-flag", form.attributes.length > 0 && form.attributes.length < 40); // IE8 } has.clearElement = function(element){ // summary: // Deletes the contents of the element passed to test functions. element.innerHTML= ""; return element; }; has.normalize = function(id, toAbsMid){ // summary: // Resolves id into a module id based on possibly-nested tenary expression that branches on has feature test value(s). // // toAbsMid: Function // Resolves a relative module id into an absolute module id var tokens = id.match(/[\?:]|[^:\?]*/g), i = 0, get = function(skip){ var term = tokens[i++]; if(term == ":"){ // empty string module name, resolves to 0 return 0; }else{ // postfixed with a ? means it is a feature to branch on, the term is the name of the feature if(tokens[i++] == "?"){ if(!skip && has(term)){ // matched the feature, get the first value from the options return get(); }else{ // did not match, get the second value, passing over the first get(true); return get(skip); } } // a module return term || 0; } }; id = get(); return id && toAbsMid(id); }; has.load = function(id, parentRequire, loaded){ // summary: // Conditional loading of AMD modules based on a has feature test value. // id: String // Gives the resolved module id to load. // parentRequire: Function // The loader require function with respect to the module that contained the plugin resource in it's // dependency list. // loaded: Function // Callback to loader that consumes result of plugin demand. if(id){ parentRequire([id], loaded); }else{ loaded(); } }; return has; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | 1 1 1 1 1 1 1 1 | define(["./_base/kernel", "require", "./_base/config", "./aspect", "./_base/lang", "./topic", "./domReady", "./sniff"], function(dojo, require, config, aspect, lang, topic, domReady, has){ // module: // dojo/hash dojo.hash = function(/* String? */ hash, /* Boolean? */ replace){ // summary: // Gets or sets the hash string in the browser URL. // description: // Handles getting and setting of location.hash. // // - If no arguments are passed, acts as a getter. // - If a string is passed, acts as a setter. // hash: // the hash is set - #string. // replace: // If true, updates the hash value in the current history // state instead of creating a new history state. // returns: // when used as a getter, returns the current hash string. // when used as a setter, returns the new hash string. // example: // | topic.subscribe("/dojo/hashchange", context, callback); // | // | function callback (hashValue){ // | // do something based on the hash value. // | } // getter if(!arguments.length){ return _getHash(); } // setter if(hash.charAt(0) == "#"){ hash = hash.substring(1); } if(replace){ _replace(hash); }else{ location.href = "#" + hash; } return hash; // String }; // Global vars var _recentHash, _ieUriMonitor, _connect, _pollFrequency = config.hashPollFrequency || 100; //Internal functions function _getSegment(str, delimiter){ var i = str.indexOf(delimiter); return (i >= 0) ? str.substring(i+1) : ""; } function _getHash(){ return _getSegment(location.href, "#"); } function _dispatchEvent(){ topic.publish("/dojo/hashchange", _getHash()); } function _pollLocation(){ if(_getHash() === _recentHash){ return; } _recentHash = _getHash(); _dispatchEvent(); } function _replace(hash){ if(_ieUriMonitor){ if(_ieUriMonitor.isTransitioning()){ setTimeout(lang.hitch(null,_replace,hash), _pollFrequency); return; } var href = _ieUriMonitor.iframe.location.href; var index = href.indexOf('?'); // main frame will detect and update itself _ieUriMonitor.iframe.location.replace(href.substring(0, index) + "?" + hash); return; } location.replace("#"+hash); !_connect && _pollLocation(); } function IEUriMonitor(){ // summary: // Determine if the browser's URI has changed or if the user has pressed the // back or forward button. If so, call _dispatchEvent. // // description: // IE doesn't add changes to the URI's hash into the history unless the hash // value corresponds to an actual named anchor in the document. To get around // this IE difference, we use a background IFrame to maintain a back-forward // history, by updating the IFrame's query string to correspond to the // value of the main browser location's hash value. // // E.g. if the value of the browser window's location changes to // // #action=someAction // // ... then we'd update the IFrame's source to: // // ?action=someAction // // This design leads to a somewhat complex state machine, which is // described below: // // ####s1 // // Stable state - neither the window's location has changed nor // has the IFrame's location. Note that this is the 99.9% case, so // we optimize for it. // // Transitions: s1, s2, s3 // // ####s2 // // Window's location changed - when a user clicks a hyperlink or // code programmatically changes the window's URI. // // Transitions: s4 // // ####s3 // // Iframe's location changed as a result of user pressing back or // forward - when the user presses back or forward, the location of // the background's iframe changes to the previous or next value in // its history. // // Transitions: s1 // // ####s4 // // IEUriMonitor has programmatically changed the location of the // background iframe, but it's location hasn't yet changed. In this // case we do nothing because we need to wait for the iframe's // location to reflect its actual state. // // Transitions: s4, s5 // // ####s5 // // IEUriMonitor has programmatically changed the location of the // background iframe, and the iframe's location has caught up with // reality. In this case we need to transition to s1. // // Transitions: s1 // // The hashchange event is always dispatched on the transition back to s1. // create and append iframe var ifr = document.createElement("iframe"), IFRAME_ID = "dojo-hash-iframe", ifrSrc = config.dojoBlankHtmlUrl || require.toUrl("./resources/blank.html"); if(config.useXDomain && !config.dojoBlankHtmlUrl){ console.warn("dojo/hash: When using cross-domain Dojo builds," + " please save dojo/resources/blank.html to your domain and set djConfig.dojoBlankHtmlUrl" + " to the path on your domain to blank.html"); } ifr.id = IFRAME_ID; ifr.src = ifrSrc + "?" + _getHash(); ifr.style.display = "none"; document.body.appendChild(ifr); this.iframe = dojo.global[IFRAME_ID]; var recentIframeQuery, transitioning, expectedIFrameQuery, docTitle, ifrOffline, iframeLoc = this.iframe.location; function resetState(){ _recentHash = _getHash(); recentIframeQuery = ifrOffline ? _recentHash : _getSegment(iframeLoc.href, "?"); transitioning = false; expectedIFrameQuery = null; } this.isTransitioning = function(){ return transitioning; }; this.pollLocation = function(){ if(!ifrOffline){ try{ //see if we can access the iframe's location without a permission denied error var iframeSearch = _getSegment(iframeLoc.href, "?"); //good, the iframe is same origin (no thrown exception) if(document.title != docTitle){ //sync title of main window with title of iframe. docTitle = this.iframe.document.title = document.title; } }catch(e){ //permission denied - server cannot be reached. ifrOffline = true; console.error("dojo/hash: Error adding history entry. Server unreachable."); } } var hash = _getHash(); if(transitioning && _recentHash === hash){ // we're in an iframe transition (s4 or s5) if(ifrOffline || iframeSearch === expectedIFrameQuery){ // s5 (iframe caught up to main window or iframe offline), transition back to s1 resetState(); _dispatchEvent(); }else{ // s4 (waiting for iframe to catch up to main window) setTimeout(lang.hitch(this,this.pollLocation),0); return; } }else if(_recentHash === hash && (ifrOffline || recentIframeQuery === iframeSearch)){ // we're in stable state (s1, iframe query == main window hash), do nothing }else{ // the user has initiated a URL change somehow. // sync iframe query <-> main window hash if(_recentHash !== hash){ // s2 (main window location changed), set iframe url and transition to s4 _recentHash = hash; transitioning = true; expectedIFrameQuery = hash; ifr.src = ifrSrc + "?" + expectedIFrameQuery; ifrOffline = false; //we're updating the iframe src - set offline to false so we can check again on next poll. setTimeout(lang.hitch(this,this.pollLocation),0); //yielded transition to s4 while iframe reloads. return; }else if(!ifrOffline){ // s3 (iframe location changed via back/forward button), set main window url and transition to s1. location.href = "#" + iframeLoc.search.substring(1); resetState(); _dispatchEvent(); } } setTimeout(lang.hitch(this,this.pollLocation), _pollFrequency); }; resetState(); // initialize state (transition to s1) setTimeout(lang.hitch(this,this.pollLocation), _pollFrequency); } domReady(function(){ if("onhashchange" in dojo.global && (!has("ie") || (has("ie") >= 8 && document.compatMode != "BackCompat"))){ //need this IE browser test because "onhashchange" exists in IE8 in IE7 mode _connect = aspect.after(dojo.global,"onhashchange",_dispatchEvent, true); }else{ if(document.addEventListener){ // Non-IE _recentHash = _getHash(); setInterval(_pollLocation, _pollFrequency); //Poll the window location for changes }else if(document.attachEvent){ // IE7- //Use hidden iframe in versions of IE that don't have onhashchange event _ieUriMonitor = new IEUriMonitor(); } // else non-supported browser, do nothing. } }); return dojo.hash; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 1 | define([ "require", // require, require.toUrl "./_base/config", // config.blankGif "./dom-class", // domClass.add "./dom-style", // domStyle.getComputedStyle "./has", "./domReady", "./_base/window" // win.body ], function(require, config, domClass, domStyle, has, domReady, win){ // module: // dojo/hccss /*===== return function(){ // summary: // Test if computer is in high contrast mode (i.e. if browser is not displaying background images). // Defines `has("highcontrast")` and sets `dj_a11y` CSS class on `<body>` if machine is in high contrast mode. // Returns `has()` method; }; =====*/ // Has() test for when background images aren't displayed. Don't call has("highcontrast") before dojo/domReady!. has.add("highcontrast", function(){ // note: if multiple documents, doesn't matter which one we use var div = win.doc.createElement("div"); div.style.cssText = "border: 1px solid; border-color:red green; position: absolute; height: 5px; top: -999px;" + "background-image: url(" + (config.blankGif || require.toUrl("./resources/blank.gif")) + ");"; win.body().appendChild(div); var cs = domStyle.getComputedStyle(div), bkImg = cs.backgroundImage, hc = (cs.borderTopColor == cs.borderRightColor) || (bkImg && (bkImg == "none" || bkImg == "url(invalid-url:)" )); if(has("ie") <= 8){ div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014 }else{ win.body().removeChild(div); } return hc; }); domReady(function(){ if(has("highcontrast")){ domClass.add(win.body(), "dj_a11y"); } }); return has; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | 1 | define(["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"], function(kernel, lang, darray, declare, dom, domConstruct, parser){ // module: // dojo/html // the parser might be needed.. // idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes var idCounter = 0; var html = { // summary: // TODOC _secureForInnerHtml: function(/*String*/ cont){ // summary: // removes !DOCTYPE and title elements from the html string. // // khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body // must go into head, so we need to cut out those tags // cont: // An html string for insertion into the dom // return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String }, // Deprecated, should use dojo/dom-constuct.empty() directly, remove in 2.0. _emptyNode: domConstruct.empty, _setNodeContent: function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont){ // summary: // inserts the given content into the given node // node: // the parent element // content: // the content to be set on the parent element. // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes // always empty domConstruct.empty(node); if(cont){ if(typeof cont == "string"){ cont = domConstruct.toDom(cont, node.ownerDocument); } if(!cont.nodeType && lang.isArrayLike(cont)){ // handle as enumerable, but it may shrink as we enumerate it for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0){ domConstruct.place( cont[i], node, "last"); } }else{ // pass nodes, documentFragments and unknowns through to dojo.place domConstruct.place(cont, node, "last"); } } // return DomNode return node; }, // we wrap up the content-setting operation in a object _ContentSetter: declare("dojo.html._ContentSetter", null, { // node: DomNode|String // An node which will be the parent element that we set content into node: "", // content: String|DomNode|DomNode[] // The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes content: "", // id: String? // Usually only used internally, and auto-generated with each instance id: "", // cleanContent: Boolean // Should the content be treated as a full html document, // and the real content stripped of <html>, <body> wrapper before injection cleanContent: false, // extractContent: Boolean // Should the content be treated as a full html document, // and the real content stripped of `<html> <body>` wrapper before injection extractContent: false, // parseContent: Boolean // Should the node by passed to the parser after the new content is set parseContent: false, // parserScope: String // Flag passed to parser. Root for attribute names to search for. If scopeName is dojo, // will search for data-dojo-type (or dojoType). For backwards compatibility // reasons defaults to dojo._scopeName (which is "dojo" except when // multi-version support is used, when it will be something like dojo16, dojo20, etc.) parserScope: kernel._scopeName, // startup: Boolean // Start the child widgets after parsing them. Only obeyed if parseContent is true. startup: true, // lifecycle methods constructor: function(/*Object*/ params, /*String|DomNode*/ node){ // summary: // Provides a configurable, extensible object to wrap the setting on content on a node // call the set() method to actually set the content.. // the original params are mixed directly into the instance "this" lang.mixin(this, params || {}); // give precedence to params.node vs. the node argument // and ensure its a node, not an id string node = this.node = dom.byId( this.node || node ); if(!this.id){ this.id = [ "Setter", (node) ? node.id || node.tagName : "", idCounter++ ].join("_"); } }, set: function(/* String|DomNode|NodeList? */ cont, /*Object?*/ params){ // summary: // front-end to the set-content sequence // cont: // An html string, node or enumerable list of nodes for insertion into the dom // If not provided, the object's content property will be used if(undefined !== cont){ this.content = cont; } // in the re-use scenario, set needs to be able to mixin new configuration if(params){ this._mixin(params); } this.onBegin(); this.setContent(); var ret = this.onEnd(); if(ret && ret.then){ // Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete. return ret; }else{ // Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to // return a Deferred like above. return this.node; } }, setContent: function(){ // summary: // sets the content on the node var node = this.node; if(!node){ // can't proceed throw new Error(this.declaredClass + ": setContent given no node"); } try{ node = html._setNodeContent(node, this.content); }catch(e){ // check if a domfault occurs when we are appending this.errorMessage // like for instance if domNode is a UL and we try append a DIV // FIXME: need to allow the user to provide a content error message string var errMess = this.onContentError(e); try{ node.innerHTML = errMess; }catch(e){ console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e); } } // always put back the node for the next method this.node = node; // DomNode }, empty: function(){ // summary: // cleanly empty out existing content // If there is a parse in progress, cancel it. if(this.parseDeferred){ if(!this.parseDeferred.isResolved()){ this.parseDeferred.cancel(); } delete this.parseDeferred; } // destroy any widgets from a previous run // NOTE: if you don't want this you'll need to empty // the parseResults array property yourself to avoid bad things happening if(this.parseResults && this.parseResults.length){ darray.forEach(this.parseResults, function(w){ if(w.destroy){ w.destroy(); } }); delete this.parseResults; } // this is fast, but if you know its already empty or safe, you could // override empty to skip this step domConstruct.empty(this.node); }, onBegin: function(){ // summary: // Called after instantiation, but before set(); // It allows modification of any of the object properties - // including the node and content provided - before the set operation actually takes place // This default implementation checks for cleanContent and extractContent flags to // optionally pre-process html string content var cont = this.content; if(lang.isString(cont)){ if(this.cleanContent){ cont = html._secureForInnerHtml(cont); } if(this.extractContent){ var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); if(match){ cont = match[1]; } } } // clean out the node and any cruft associated with it - like widgets this.empty(); this.content = cont; return this.node; // DomNode }, onEnd: function(){ // summary: // Called after set(), when the new content has been pushed into the node // It provides an opportunity for post-processing before handing back the node to the caller // This default implementation checks a parseContent flag to optionally run the dojo parser over the new content if(this.parseContent){ // populates this.parseResults and this.parseDeferred if you need those.. this._parse(); } return this.node; // DomNode // TODO: for 2.0 return a Promise indicating that the parse completed. }, tearDown: function(){ // summary: // manually reset the Setter instance if its being re-used for example for another set() // description: // tearDown() is not called automatically. // In normal use, the Setter instance properties are simply allowed to fall out of scope // but the tearDown method can be called to explicitly reset this instance. delete this.parseResults; delete this.parseDeferred; delete this.node; delete this.content; }, onContentError: function(err){ return "Error occurred setting content: " + err; }, onExecError: function(err){ return "Error occurred executing scripts: " + err; }, _mixin: function(params){ // mix properties/methods into the instance // TODO: the intention with tearDown is to put the Setter's state // back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params) // so we could do something here to move the original properties aside for later restoration var empty = {}, key; for(key in params){ if(key in empty){ continue; } // TODO: here's our opportunity to mask the properties we don't consider configurable/overridable // .. but history shows we'll almost always guess wrong this[key] = params[key]; } }, _parse: function(){ // summary: // runs the dojo parser over the node contents, storing any results in this.parseResults // and the parse promise in this.parseDeferred // Any errors resulting from parsing are passed to _onError for handling var rootNode = this.node; try{ // store the results (widgets, whatever) for potential retrieval var inherited = {}; darray.forEach(["dir", "lang", "textDir"], function(name){ if(this[name]){ inherited[name] = this[name]; } }, this); var self = this; this.parseDeferred = parser.parse({ rootNode: rootNode, noStart: !this.startup, inherited: inherited, scope: this.parserScope }).then(function(results){ return self.parseResults = results; }, function(e){ self._onError('Content', e, "Error parsing in _ContentSetter#" + this.id); }); }catch(e){ this._onError('Content', e, "Error parsing in _ContentSetter#" + this.id); } }, _onError: function(type, err, consoleText){ // summary: // shows user the string that is returned by on[type]Error // override/implement on[type]Error and return your own string to customize var errText = this['on' + type + 'Error'].call(this, err); if(consoleText){ console.error(consoleText, err); }else if(errText){ // a empty string won't change current content html._setNodeContent(this.node, errText, true); } } }), // end declare() set: function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont, /*Object?*/ params){ // summary: // inserts (replaces) the given content into the given node. dojo/dom-construct.place(cont, node, "only") // may be a better choice for simple HTML insertion. // description: // Unless you need to use the params capabilities of this method, you should use // dojo/dom-construct.place(cont, node, "only"). dojo/dom-construct..place() has more robust support for injecting // an HTML string into the DOM, but it only handles inserting an HTML string as DOM // elements, or inserting a DOM node. dojo/dom-construct..place does not handle NodeList insertions // dojo/dom-construct.place(cont, node, "only"). dojo/dom-construct.place() has more robust support for injecting // an HTML string into the DOM, but it only handles inserting an HTML string as DOM // elements, or inserting a DOM node. dojo/dom-construct.place does not handle NodeList insertions // or the other capabilities as defined by the params object for this method. // node: // the parent element that will receive the content // cont: // the content to be set on the parent element. // This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes // params: // Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter // example: // A safe string/node/nodelist content replacement/injection with hooks for extension // Example Usage: // | html.set(node, "some string"); // | html.set(node, contentNode, {options}); // | html.set(node, myNode.childNodes, {options}); if(undefined == cont){ console.warn("dojo.html.set: no cont argument provided, using empty string"); cont = ""; } if(!params){ // simple and fast return html._setNodeContent(node, cont, true); }else{ // more options but slower // note the arguments are reversed in order, to match the convention for instantiation via the parser var op = new html._ContentSetter(lang.mixin( params, { content: cont, node: node } )); return op.set(); } } }; lang.setObject("dojo.html", html); return html; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 | 1 1 1 1 1 1 1 1 1 | define(["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./has!host-browser?./_base/xhr", "./json", "module"], function(dojo, require, has, array, config, lang, xhr, json, module){ // module: // dojo/i18n has.add("dojo-preload-i18n-Api", // if true, define the preload localizations machinery 1 ); has.add("dojo-v1x-i18n-Api", // if true, define the v1.x i18n functions 1 ); var thisModule = dojo.i18n = { // summary: // This module implements the dojo/i18n! plugin and the v1.6- i18n API // description: // We choose to include our own plugin to leverage functionality already contained in dojo // and thereby reduce the size of the plugin compared to various loader implementations. Also, this // allows foreign AMD loaders to be used without their plugins. }, nlsRe = // regexp for reconstructing the master bundle name from parts of the regexp match // nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives: // ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"] // nlsRe.exec("foo/bar/baz/nls/foo") gives: // ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""] // so, if match[5] is blank, it means this is the top bundle definition. // courtesy of http://requirejs.org /(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/, getAvailableLocales = function( root, locale, bundlePath, bundleName ){ // summary: // return a vector of module ids containing all available locales with respect to the target locale // For example, assuming: // // - the root bundle indicates specific bundles for "fr" and "fr-ca", // - bundlePath is "myPackage/nls" // - bundleName is "myBundle" // // Then a locale argument of "fr-ca" would return // // ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"] // // Notice that bundles are returned least-specific to most-specific, starting with the root. // // If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales; // therefore, assume everything is available and get 404 errors that indicate a particular localization is not available for(var result = [bundlePath + bundleName], localeParts = locale.split("-"), current = "", i = 0; i<localeParts.length; i++){ current += (current ? "-" : "") + localeParts[i]; if(!root || root[current]){ result.push(bundlePath + current + "/" + bundleName); result.specificity = current; } } return result; }, cache = {}, getBundleName = function(moduleName, bundleName, locale){ locale = locale ? locale.toLowerCase() : dojo.locale; moduleName = moduleName.replace(/\./g, "/"); bundleName = bundleName.replace(/\./g, "/"); return (/root/i.test(locale)) ? (moduleName + "/nls/" + bundleName) : (moduleName + "/nls/" + locale + "/" + bundleName); }, getL10nName = dojo.getL10nName = function(moduleName, bundleName, locale){ return moduleName = module.id + "!" + getBundleName(moduleName, bundleName, locale); }, doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){ // summary: // get the root bundle which instructs which other bundles are required to construct the localized bundle require([bundlePathAndName], function(root){ var current = lang.clone(root.root || root.ROOT),// 1.6 built bundle defined ROOT availableLocales = getAvailableLocales(!root._v1x && root, locale, bundlePath, bundleName); require(availableLocales, function(){ for (var i = 1; i<availableLocales.length; i++){ current = lang.mixin(lang.clone(current), arguments[i]); } // target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested) var target = bundlePathAndName + "/" + locale; cache[target] = current; current.$locale = availableLocales.specificity; load(); }); }); }, normalize = function(id, toAbsMid){ // summary: // id may be relative. // preload has form `*preload*<path>/nls/<module>*<flattened locales>` and // therefore never looks like a relative return /^\./.test(id) ? toAbsMid(id) : id; }, getLocalesToLoad = function(targetLocale){ var list = config.extraLocale || []; list = lang.isArray(list) ? list : [list]; list.push(targetLocale); return list; }, load = function(id, require, load){ // summary: // id is in one of the following formats // // 1. <path>/nls/<bundle> // => load the bundle, localized to config.locale; load all bundles localized to // config.extraLocale (if any); return the loaded bundle localized to config.locale. // // 2. <path>/nls/<locale>/<bundle> // => load then return the bundle localized to <locale> // // 3. *preload*<path>/nls/<module>*<JSON array of available locales> // => for config.locale and all config.extraLocale, load all bundles found // in the best-matching bundle rollup. A value of 1 is returned, which // is meaningless other than to say the plugin is executing the requested // preloads // // In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see // normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder. // // To load a bundle means to insert the bundle into the plugin's cache and publish the bundle // value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key // // <path>/nls/<bundle>/<locale> // // will hold the value. Similarly, then plugin will publish this value to the loader by // // define("<path>/nls/<bundle>/<locale>", <bundle-value>); // // Given this algorithm, other machinery can provide fast load paths be preplacing // values in the plugin's cache, which is public. When a load is demanded the // cache is inspected before starting any loading. Explicitly placing values in the plugin // cache is an advanced/experimental feature that should not be needed; use at your own risk. // // For the normal AMD algorithm, the root bundle is loaded first, which instructs the // plugin what additional localized bundles are required for a particular locale. These // additional locales are loaded and a mix of the root and each progressively-specific // locale is returned. For example: // // 1. The client demands "dojo/i18n!some/path/nls/someBundle // // 2. The loader demands load(some/path/nls/someBundle) // // 3. This plugin require's "some/path/nls/someBundle", which is the root bundle. // // 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations // are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin // requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle" // // 5. Upon receiving all required bundles, the plugin constructs the value of the bundle // ab-cd-ef as... // // mixin(mixin(mixin({}, require("some/path/nls/someBundle"), // require("some/path/nls/ab/someBundle")), // require("some/path/nls/ab-cd-ef/someBundle")); // // This value is inserted into the cache and published to the loader at the // key/module-id some/path/nls/someBundle/ab-cd-ef. // // The special preload signature (case 3) instructs the plugin to stop servicing all normal requests // (further preload requests will be serviced) until all ongoing preloading has completed. // // The preload signature instructs the plugin that a special rollup module is available that contains // one or more flattened, localized bundles. The JSON array of available locales indicates which locales // are available. Here is an example: // // *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"] // // This indicates the following rollup modules are available: // // some/path/nls/someModule_ROOT // some/path/nls/someModule_ab // some/path/nls/someModule_ab-cd-ef // // Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash. // For example, assume someModule contained the bundles some/bundle/path/someBundle and // some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows: // // define({ // some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>, // some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>, // }); // // E.g., given this design, preloading for locale=="ab" can execute the following algorithm: // // require(["some/path/nls/someModule_ab"], function(rollup){ // for(var p in rollup){ // var id = p + "/ab", // cache[id] = rollup[p]; // define(id, rollup[p]); // } // }); // // Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and // load accordingly. // // The builder will write such rollups for every layer if a non-empty localeList profile property is // provided. Further, the builder will include the following cache entry in the cache associated with // any layer. // // "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);} // // The *now special cache module instructs the loader to apply the provided function to context-require // with respect to the particular layer being defined. This causes the plugin to hold all normal service // requests until all preloading is complete. // // Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case // where the target locale has a single segment and a layer depends on a single bundle: // // Without Preloads: // // 1. Layer loads root bundle. // 2. bundle is demanded; plugin loads single localized bundle. // // With Preloads: // // 1. Layer causes preloading of target bundle. // 2. bundle is demanded; service is delayed until preloading complete; bundle is returned. // // In each case a single transaction is required to load the target bundle. In cases where multiple bundles // are required and/or the locale has multiple segments, preloads still requires a single transaction whereas // the normal path requires an additional transaction for each additional bundle/locale-segment. However all // of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading // algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false. if(has("dojo-preload-i18n-Api")){ var split = id.split("*"), preloadDemand = split[1] == "preload"; if(preloadDemand){ if(!cache[id]){ // use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but // who knows what over-aggressive human optimizers may attempt cache[id] = 1; preloadL10n(split[2], json.parse(split[3]), 1, require); } // don't stall the loader! load(1); } if(preloadDemand || waitForPreloads(id, require, load)){ return; } } var match = nlsRe.exec(id), bundlePath = match[1] + "/", bundleName = match[5] || match[4], bundlePathAndName = bundlePath + bundleName, localeSpecified = (match[5] && match[4]), targetLocale = localeSpecified || dojo.locale || "", loadTarget = bundlePathAndName + "/" + targetLocale, loadList = localeSpecified ? [targetLocale] : getLocalesToLoad(targetLocale), remaining = loadList.length, finish = function(){ if(!--remaining){ load(lang.delegate(cache[loadTarget])); } }; array.forEach(loadList, function(locale){ var target = bundlePathAndName + "/" + locale; if(has("dojo-preload-i18n-Api")){ checkForLegacyModules(target); } if(!cache[target]){ doLoad(require, bundlePathAndName, bundlePath, bundleName, locale, finish); }else{ finish(); } }); }; if(has("dojo-unit-tests")){ var unitTests = thisModule.unitTests = []; } if(has("dojo-preload-i18n-Api") || has("dojo-v1x-i18n-Api")){ var normalizeLocale = thisModule.normalizeLocale = function(locale){ var result = locale ? locale.toLowerCase() : dojo.locale; return result == "root" ? "ROOT" : result; }, isXd = function(mid, contextRequire){ return (has("dojo-sync-loader") && has("dojo-v1x-i18n-Api")) ? contextRequire.isXdUrl(require.toUrl(mid + ".js")) : true; }, preloading = 0, preloadWaitQueue = [], preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean?*/ guaranteedAmdFormat, /*function?*/ contextRequire){ // summary: // Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any) // description: // Only called by built layer files. The entire locale hierarchy is loaded. For example, // if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6- // in that the v1.6- would only load ab-cd...which was *always* flattened. // // If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm // and the extra possible extra transaction. // If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function // needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which // itself may have been mapped. contextRequire = contextRequire || require; function doRequire(mid, callback){ if(isXd(mid, contextRequire) || guaranteedAmdFormat){ contextRequire([mid], callback); }else{ syncRequire([mid], callback, contextRequire); } } function forEachLocale(locale, func){ // given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy var parts = locale.split("-"); while(parts.length){ if(func(parts.join("-"))){ return; } parts.pop(); } func("ROOT"); } function preloadingAddLock(){ preloading++; } function preloadingRelLock(){ --preloading; while(!preloading && preloadWaitQueue.length){ load.apply(null, preloadWaitQueue.shift()); } } function cacheId(path, name, loc, require){ // path is assumed to have a trailing "/" return require.toAbsMid(path + name + "/" + loc) } function preload(locale){ locale = normalizeLocale(locale); forEachLocale(locale, function(loc){ if(array.indexOf(localesGenerated, loc) >= 0){ var mid = bundlePrefix.replace(/\./g, "/") + "_" + loc; preloadingAddLock(); doRequire(mid, function(rollup){ for(var p in rollup){ var bundle = rollup[p], match = p.match(/(.+)\/([^\/]+)$/), bundleName, bundlePath; // If there is no match, the bundle is not a regular bundle from an AMD layer. if (!match){continue;} bundleName = match[2]; bundlePath = match[1] + "/"; // backcompat bundle._localized = bundle._localized || {}; var localized; if(loc === "ROOT"){ var root = localized = bundle._localized; delete bundle._localized; root.root = bundle; cache[require.toAbsMid(p)] = root; }else{ localized = bundle._localized; cache[cacheId(bundlePath, bundleName, loc, require)] = bundle; } if(loc !== locale){ // capture some locale variables function improveBundle(bundlePath, bundleName, bundle, localized){ // locale was not flattened and we've fallen back to a less-specific locale that was flattened // for example, we had a flattened 'fr', a 'fr-ca' is available for at least this bundle, and // locale==='fr-ca'; therefore, we must improve the bundle as retrieved from the rollup by // manually loading the fr-ca version of the bundle and mixing this into the already-retrieved 'fr' // version of the bundle. // // Remember, different bundles may have different sets of locales available. // // we are really falling back on the regular algorithm here, but--hopefully--starting with most // of the required bundles already on board as given by the rollup and we need to "manually" load // only one locale from a few bundles...or even better...we won't find anything better to load. // This algorithm ensures there is nothing better to load even when we can only load a less-specific rollup. // // note: this feature is only available in async mode // inspect the loaded bundle that came from the rollup to see if something better is available // for any bundle in a rollup, more-specific available locales are given at localized. var requiredBundles = [], cacheIds = []; forEachLocale(locale, function(loc){ if(localized[loc]){ requiredBundles.push(require.toAbsMid(bundlePath + loc + "/" + bundleName)); cacheIds.push(cacheId(bundlePath, bundleName, loc, require)); } }); if(requiredBundles.length){ preloadingAddLock(); contextRequire(requiredBundles, function(){ for(var i = 0; i < requiredBundles.length; i++){ bundle = lang.mixin(lang.clone(bundle), arguments[i]); cache[cacheIds[i]] = bundle; } // this is the best possible (maybe a perfect match, maybe not), accept it cache[cacheId(bundlePath, bundleName, locale, require)] = lang.clone(bundle); preloadingRelLock(); }); }else{ // this is the best possible (definitely not a perfect match), accept it cache[cacheId(bundlePath, bundleName, locale, require)] = bundle; } } improveBundle(bundlePath, bundleName, bundle, localized); } } preloadingRelLock(); }); return true; } return false; }); } preload(); array.forEach(dojo.config.extraLocale, preload); }, waitForPreloads = function(id, require, load){ if(preloading){ preloadWaitQueue.push([id, require, load]); } return preloading; }, checkForLegacyModules = function() {}; } if(has("dojo-v1x-i18n-Api")){ // this code path assumes the dojo loader and won't work with a standard AMD loader var amdValue = {}, evalBundle = // use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary) new Function( "__bundle", // the bundle to evalutate "__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space "__mid", // the mid that __bundle is intended to define "__amdValue", // returns one of: // 1 => the bundle was an AMD bundle // a legacy bundle object that is the value of __mid // instance of Error => could not figure out how to evaluate bundle // used to detect when __bundle calls define "var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;}," + " require = function(){define.called = 1;};" + "try{" + "define.called = 0;" + "eval(__bundle);" + "if(define.called==1)" // bundle called define; therefore signal it's an AMD bundle + "return __amdValue;" + "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))" // bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space + "return __checkForLegacyModules;" + "}catch(e){}" // evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle // either way, re-eval *after* surrounding with parentheses + "try{" + "return eval('('+__bundle+')');" + "}catch(e){" + "return e;" + "}" ), syncRequire = function(deps, callback, require){ var results = []; array.forEach(deps, function(mid){ var url = require.toUrl(mid + ".js"); function load(text){ var result = evalBundle(text, checkForLegacyModules, mid, amdValue); if(result===amdValue){ // the bundle was an AMD module; re-inject it through the normal AMD path // we gotta do this since it could be an anonymous module and simply evaluating // the text here won't provide the loader with the context to know what // module is being defined()'d. With browser caching, this should be free; further // this entire code path can be circumvented by using the AMD format to begin with results.push(cache[url] = amdValue.result); }else{ if(result instanceof Error){ console.error("failed to evaluate i18n bundle; url=" + url, result); result = {}; } // nls/<locale>/<bundle-name> indicates not the root. results.push(cache[url] = (/nls\/[^\/]+\/[^\/]+$/.test(url) ? result : {root:result, _v1x:1})); } } if(cache[url]){ results.push(cache[url]); }else{ var bundle = require.syncLoadNls(mid); // don't need to check for legacy since syncLoadNls returns a module if the module // (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called // from getLocalization --> load, then load will have called checkForLegacyModules() before // calling syncRequire; if syncRequire is called from preloadLocalizations, then we // don't care about checkForLegacyModules() because that will be done when a particular // bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant // because cached modules are always v1.7+ built modules. if(bundle){ results.push(bundle); }else{ if(!xhr){ try{ require.getText(url, true, load); }catch(e){ results.push(cache[url] = {}); } }else{ xhr.get({ url:url, sync:true, load:load, error:function(){ results.push(cache[url] = {}); } }); } } } }); callback && callback.apply(null, results); }; checkForLegacyModules = function(target){ // legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache for(var result, names = target.split("/"), object = dojo.global[names[0]], i = 1; object && i<names.length-1; object = object[names[i++]]){} if(object){ result = object[names[i]]; if(!result){ // fallback for incorrect bundle build of 1.6 result = object[names[i].replace(/-/g,"_")]; } if(result){ cache[target] = result; } } return result; }; thisModule.getLocalization = function(moduleName, bundleName, locale){ var result, l10nName = getBundleName(moduleName, bundleName, locale); load( l10nName, // isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module. // Since this legacy function does not have the concept of a reference module, resolve with respect to this // dojo/i18n module, which, itself may have been mapped. (!isXd(l10nName, require) ? function(deps, callback){ syncRequire(deps, callback, require); } : require), function(result_){ result = result_; } ); return result; }; if(has("dojo-unit-tests")){ unitTests.push(function(doh){ doh.register("tests.i18n.unit", function(t){ var check; check = evalBundle("{prop:1}", checkForLegacyModules, "nonsense", amdValue); t.is({prop:1}, check); t.is(undefined, check[1]); check = evalBundle("({prop:1})", checkForLegacyModules, "nonsense", amdValue); t.is({prop:1}, check); t.is(undefined, check[1]); check = evalBundle("{'prop-x':1}", checkForLegacyModules, "nonsense", amdValue); t.is({'prop-x':1}, check); t.is(undefined, check[1]); check = evalBundle("({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue); t.is({'prop-x':1}, check); t.is(undefined, check[1]); check = evalBundle("define({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue); t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result); check = evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules, "nonsense", amdValue); t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result); check = evalBundle("this is total nonsense and should throw an error", checkForLegacyModules, "nonsense", amdValue); t.is(check instanceof Error, true); }); }); } } return lang.mixin(thisModule, { dynamic:true, normalize:normalize, load:load, cache:cache, getL10nName: getL10nName }); }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | 1 | define(["./_base/lang"], function(lang){ // module: // dojo/io-query var backstop = {}; return { // summary: // This module defines query string processing functions. objectToQuery: function objectToQuery(/*Object*/ map){ // summary: // takes a name/value mapping object and returns a string representing // a URL-encoded version of that object. // example: // this object: // // | { // | blah: "blah", // | multi: [ // | "thud", // | "thonk" // | ] // | }; // // yields the following query string: // // | "blah=blah&multi=thud&multi=thonk" // FIXME: need to implement encodeAscii!! var enc = encodeURIComponent, pairs = []; for(var name in map){ var value = map[name]; if(value != backstop[name]){ var assign = enc(name) + "="; if(lang.isArray(value)){ for(var i = 0, l = value.length; i < l; ++i){ pairs.push(assign + enc(value[i])); } }else{ pairs.push(assign + enc(value)); } } } return pairs.join("&"); // String }, queryToObject: function queryToObject(/*String*/ str){ // summary: // Create an object representing a de-serialized query section of a // URL. Query keys with multiple values are returned in an array. // // example: // This string: // // | "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&" // // results in this object structure: // // | { // | foo: [ "bar", "baz" ], // | thinger: " spaces =blah", // | zonk: "blarg" // | } // // Note that spaces and other urlencoded entities are correctly // handled. // FIXME: should we grab the URL string if we're not passed one? var dec = decodeURIComponent, qp = str.split("&"), ret = {}, name, val; for(var i = 0, l = qp.length, item; i < l; ++i){ item = qp[i]; if(item.length){ var s = item.indexOf("="); if(s < 0){ name = dec(item); val = ""; }else{ name = dec(item.slice(0, s)); val = dec(item.slice(s + 1)); } if(typeof ret[name] == "string"){ // inline'd type check ret[name] = [ret[name]]; } if(lang.isArray(ret[name])){ ret[name].push(val); }else{ ret[name] = val; } } } return ret; // Object } }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 1 1 | define(["./has"], function(has){ "use strict"; var hasJSON = typeof JSON != "undefined"; has.add("json-parse", hasJSON); // all the parsers work fine // Firefox 3.5/Gecko 1.9 fails to use replacer in stringify properly https://bugzilla.mozilla.org/show_bug.cgi?id=509184 has.add("json-stringify", hasJSON && JSON.stringify({a:0}, function(k,v){return v||1;}) == '{"a":1}'); /*===== return { // summary: // Functions to parse and serialize JSON parse: function(str, strict){ // summary: // Parses a [JSON](http://json.org) string to return a JavaScript object. // description: // This function follows [native JSON API](https://developer.mozilla.org/en/JSON) // Throws for invalid JSON strings. This delegates to eval() if native JSON // support is not available. By default this will evaluate any valid JS expression. // With the strict parameter set to true, the parser will ensure that only // valid JSON strings are parsed (otherwise throwing an error). Without the strict // parameter, the content passed to this method must come // from a trusted source. // str: // a string literal of a JSON item, for instance: // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'` // strict: // When set to true, this will ensure that only valid, secure JSON is ever parsed. // Make sure this is set to true for untrusted content. Note that on browsers/engines // without native JSON support, setting this to true will run slower. }, stringify: function(value, replacer, spacer){ // summary: // Returns a [JSON](http://json.org) serialization of an object. // description: // Returns a [JSON](http://json.org) serialization of an object. // This function follows [native JSON API](https://developer.mozilla.org/en/JSON) // Note that this doesn't check for infinite recursion, so don't do that! // value: // A value to be serialized. // replacer: // A replacer function that is called for each value and can return a replacement // spacer: // A spacer string to be used for pretty printing of JSON // example: // simple serialization of a trivial object // | define(["dojo/json"], function(JSON){ // | var jsonStr = JSON.stringify({ howdy: "stranger!", isStrange: true }); // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr); } }; =====*/ if(has("json-stringify")){ return JSON; }else{ var escapeString = function(/*String*/str){ // summary: // Adds escape sequences for non-visual characters, double quote and // backslash and surrounds with double quotes to form a valid string // literal. return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'). replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n"). replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string }; return { parse: has("json-parse") ? JSON.parse : function(str, strict){ if(strict && !/^([\s\[\{]*(?:"(?:\\.|[^"])*"|-?\d[\d\.]*(?:[Ee][+-]?\d+)?|null|true|false|)[\s\]\}]*(?:,|:|$))+$/.test(str)){ throw new SyntaxError("Invalid characters in JSON"); } return eval('(' + str + ')'); }, stringify: function(value, replacer, spacer){ var undef; if(typeof replacer == "string"){ spacer = replacer; replacer = null; } function stringify(it, indent, key){ if(replacer){ it = replacer(key, it); } var val, objtype = typeof it; if(objtype == "number"){ return isFinite(it) ? it + "" : "null"; } if(objtype == "boolean"){ return it + ""; } if(it === null){ return "null"; } if(typeof it == "string"){ return escapeString(it); } if(objtype == "function" || objtype == "undefined"){ return undef; // undefined } // short-circuit for objects that support "json" serialization // if they return "self" then just pass-through... if(typeof it.toJSON == "function"){ return stringify(it.toJSON(key), indent, key); } if(it instanceof Date){ return '"{FullYear}-{Month+}-{Date}T{Hours}:{Minutes}:{Seconds}Z"'.replace(/\{(\w+)(\+)?\}/g, function(t, prop, plus){ var num = it["getUTC" + prop]() + (plus ? 1 : 0); return num < 10 ? "0" + num : num; }); } if(it.valueOf() !== it){ // primitive wrapper, try again unwrapped: return stringify(it.valueOf(), indent, key); } var nextIndent= spacer ? (indent + spacer) : ""; /* we used to test for DOM nodes and throw, but FF serializes them as {}, so cross-browser consistency is probably not efficiently attainable */ var sep = spacer ? " " : ""; var newLine = spacer ? "\n" : ""; // array if(it instanceof Array){ var itl = it.length, res = []; for(key = 0; key < itl; key++){ var obj = it[key]; val = stringify(obj, nextIndent, key); if(typeof val != "string"){ val = "null"; } res.push(newLine + nextIndent + val); } return "[" + res.join(",") + newLine + indent + "]"; } // generic object code path var output = []; for(key in it){ var keyStr; if(it.hasOwnProperty(key)){ if(typeof key == "number"){ keyStr = '"' + key + '"'; }else if(typeof key == "string"){ keyStr = escapeString(key); }else{ // skip non-string or number keys continue; } val = stringify(it[key], nextIndent, key); if(typeof val != "string"){ // skip non-serializable values continue; } // At this point, the most non-IE browsers don't get in this branch // (they have native JSON), so push is definitely the way to output.push(newLine + nextIndent + keyStr + ":" + sep + val); } } return "{" + output.join(",") + newLine + indent + "}"; // String } return stringify(value, "", ""); } }; } }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 1 | define(["./_base/kernel", "./sniff"], function(dojo, has){ // module: // dojo/keys return dojo.keys = { // summary: // Definitions for common key values. Client code should test keyCode against these named constants, // as the actual codes can vary by browser. BACKSPACE: 8, TAB: 9, CLEAR: 12, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, META: has("webkit") ? 91 : 224, // the apple key on macs PAUSE: 19, CAPS_LOCK: 20, ESCAPE: 27, SPACE: 32, PAGE_UP: 33, PAGE_DOWN: 34, END: 35, HOME: 36, LEFT_ARROW: 37, UP_ARROW: 38, RIGHT_ARROW: 39, DOWN_ARROW: 40, INSERT: 45, DELETE: 46, HELP: 47, LEFT_WINDOW: 91, RIGHT_WINDOW: 92, SELECT: 93, NUMPAD_0: 96, NUMPAD_1: 97, NUMPAD_2: 98, NUMPAD_3: 99, NUMPAD_4: 100, NUMPAD_5: 101, NUMPAD_6: 102, NUMPAD_7: 103, NUMPAD_8: 104, NUMPAD_9: 105, NUMPAD_MULTIPLY: 106, NUMPAD_PLUS: 107, NUMPAD_ENTER: 108, NUMPAD_MINUS: 109, NUMPAD_PERIOD: 110, NUMPAD_DIVIDE: 111, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, F13: 124, F14: 125, F15: 126, NUM_LOCK: 144, SCROLL_LOCK: 145, UP_DPAD: 175, DOWN_DPAD: 176, LEFT_DPAD: 177, RIGHT_DPAD: 178, // virtual key mapping copyKey: has("mac") && !has("air") ? (has("safari") ? 91 : 224 ) : 17 }; }); |
| 1 2 3 4 5 6 7 8 9 | 1 | define(["./_base/loader"], function(loader){ return { dynamic:0, normalize:function(id){return id;}, load:loader.loadInit }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 1 | define([ "./_base/kernel", // kernel.isAsync "./has", "require", "./sniff", "./_base/lang", "./_base/array", "./_base/config", "./ready", "./_base/declare", "./_base/connect", "./_base/Deferred", "./_base/json", "./_base/Color", "./has!dojo-firebug?./_firebug/firebug", "./has!host-browser?./_base/browser", "./has!dojo-sync-loader?./_base/loader" ], function(kernel, has, require, sniff, lang, array, config, ready){ // module: // dojo/main // summary: // This is the package main module for the dojo package; it loads dojo base appropriate for the execution environment. // the preferred way to load the dojo firebug console is by setting has("dojo-firebug") true in dojoConfig // the isDebug config switch is for backcompat and will work fine in sync loading mode; it works in // async mode too, but there's no guarantee when the module is loaded; therefore, if you need a firebug // console guaranteed at a particular spot in an app, either set config.has["dojo-firebug"] true before // loading dojo.js or explicitly include dojo/_firebug/firebug in a dependency list. if(config.isDebug){ require(["./_firebug/firebug"]); } // dojoConfig.require is deprecated; use the loader configuration property deps has.add("dojo-config-require", 1); if(has("dojo-config-require")){ var deps= config.require; if(deps){ // config.require may be dot notation deps= array.map(lang.isArray(deps) ? deps : [deps], function(item){ return item.replace(/\./g, "/"); }); if(kernel.isAsync){ require(deps); }else{ // this is a bit janky; in 1.6- dojo is defined before these requires are applied; but in 1.7+ // dojo isn't defined until returning from this module; this is only a problem in sync mode // since we're in sync mode, we know we've got our loader with its priority ready queue ready(1, function(){require(deps);}); } } } return kernel; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | 1 1 | define(["./_base/kernel", "./on", "./has", "./dom", "./_base/window"], function(dojo, on, has, dom, win){ // module: // dojo/mouse has.add("dom-quirks", win.doc && win.doc.compatMode == "BackCompat"); has.add("events-mouseenter", win.doc && "onmouseenter" in win.doc.createElement("div")); has.add("events-mousewheel", win.doc && 'onmousewheel' in win.doc); var mouseButtons; if((has("dom-quirks") && has("ie")) || !has("dom-addeventlistener")){ mouseButtons = { LEFT: 1, MIDDLE: 4, RIGHT: 2, // helper functions isButton: function(e, button){ return e.button & button; }, isLeft: function(e){ return e.button & 1; }, isMiddle: function(e){ return e.button & 4; }, isRight: function(e){ return e.button & 2; } }; }else{ mouseButtons = { LEFT: 0, MIDDLE: 1, RIGHT: 2, // helper functions isButton: function(e, button){ return e.button == button; }, isLeft: function(e){ return e.button == 0; }, isMiddle: function(e){ return e.button == 1; }, isRight: function(e){ return e.button == 2; } }; } dojo.mouseButtons = mouseButtons; /*===== dojo.mouseButtons = { // LEFT: Number // Numeric value of the left mouse button for the platform. LEFT: 0, // MIDDLE: Number // Numeric value of the middle mouse button for the platform. MIDDLE: 1, // RIGHT: Number // Numeric value of the right mouse button for the platform. RIGHT: 2, isButton: function(e, button){ // summary: // Checks an event object for a pressed button // e: Event // Event object to examine // button: Number // The button value (example: dojo.mouseButton.LEFT) return e.button == button; // Boolean }, isLeft: function(e){ // summary: // Checks an event object for the pressed left button // e: Event // Event object to examine return e.button == 0; // Boolean }, isMiddle: function(e){ // summary: // Checks an event object for the pressed middle button // e: Event // Event object to examine return e.button == 1; // Boolean }, isRight: function(e){ // summary: // Checks an event object for the pressed right button // e: Event // Event object to examine return e.button == 2; // Boolean } }; =====*/ function eventHandler(type, selectHandler){ // emulation of mouseenter/leave with mouseover/out using descendant checking var handler = function(node, listener){ return on(node, type, function(evt){ if(selectHandler){ return selectHandler(evt, listener); } if(!dom.isDescendant(evt.relatedTarget, node)){ return listener.call(this, evt); } }); }; handler.bubble = function(select){ return eventHandler(type, function(evt, listener){ // using a selector, use the select function to determine if the mouse moved inside the selector and was previously outside the selector var target = select(evt.target); var relatedTarget = evt.relatedTarget; if(target && (target != (relatedTarget && relatedTarget.nodeType == 1 && select(relatedTarget)))){ return listener.call(target, evt); } }); }; return handler; } var wheel; if(has("events-mousewheel")){ wheel = 'mousewheel'; }else{ //firefox wheel = function(node, listener){ return on(node, 'DOMMouseScroll', function(evt){ evt.wheelDelta = -evt.detail; listener.call(this, evt); }); }; } return { // summary: // This module provide mouse event handling utility functions and exports // mouseenter and mouseleave event emulation. // example: // To use these events, you register a mouseenter like this: // | define(["dojo/on", dojo/mouse"], function(on, mouse){ // | on(targetNode, mouse.enter, function(event){ // | dojo.addClass(targetNode, "highlighted"); // | }); // | on(targetNode, mouse.leave, function(event){ // | dojo.removeClass(targetNode, "highlighted"); // | }); _eventHandler: eventHandler, // for dojo/touch // enter: Synthetic Event // This is an extension event for the mouseenter that IE provides, emulating the // behavior on other browsers. enter: eventHandler("mouseover"), // leave: Synthetic Event // This is an extension event for the mouseleave that IE provides, emulating the // behavior on other browsers. leave: eventHandler("mouseout"), // wheel: Normalized Mouse Wheel Event // This is an extension event for the mousewheel that non-Mozilla browsers provide, // emulating the behavior on Mozilla based browsers. wheel: wheel, isLeft: mouseButtons.isLeft, /*===== isLeft: function(){ // summary: // Test an event object (from a mousedown event) to see if the left button was pressed. }, =====*/ isMiddle: mouseButtons.isMiddle, /*===== isMiddle: function(){ // summary: // Test an event object (from a mousedown event) to see if the middle button was pressed. }, =====*/ isRight: mouseButtons.isRight /*===== , isRight: function(){ // summary: // Test an event object (from a mousedown event) to see if the right button was pressed. } =====*/ }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 1 | define(["./has"], function(has){ if(!has("host-node")){ throw new Error("node plugin failed to load because environment is not Node.js"); } var pathUtil; if(require.nodeRequire){ pathUtil = require.nodeRequire("path"); }else{ throw new Error("node plugin failed to load because it cannot find the original Node.js require"); } return { // summary: // This AMD plugin module allows native Node.js modules to be loaded by AMD modules using the Dojo // loader. Note that this plugin will not work with AMD loaders other than the Dojo loader. // example: // | require(["dojo/node!fs"], function(fs){ // | var fileData = fs.readFileSync("foo.txt", "utf-8"); // | }); load: function(/*string*/ id, /*Function*/ require, /*Function*/ load){ // summary: // Standard AMD plugin interface. See https://github.com/amdjs/amdjs-api/wiki/Loader-Plugins // for information. if(!require.nodeRequire){ throw new Error("Cannot find native require function"); } load((function(id, require){ var oldDefine = define, result; // Some modules may attempt to detect an AMD loader via define and define.amd. This can cause issues // when other CommonJS modules attempt to load them via the standard node require(). If define is // temporarily moved into another variable, it will prevent modules from detecting AMD in this fashion. define = undefined; try{ result = require(id); }finally{ define = oldDefine; } return result; })(id, require.nodeRequire)); }, normalize: function (/**string*/ id, /*Function*/ normalize){ // summary: // Produces a normalized id to be used by node. Relative ids are resolved relative to the requesting // module's location in the file system and will return an id with path separators appropriate for the // local file system. if(id.charAt(0) === "."){ // dirname of the reference module - normalized to match the local file system var referenceModuleDirname = require.toUrl(normalize(".")).replace("/", pathUtil.sep), segments = id.split("/"); segments.unshift(referenceModuleDirname); // this will produce an absolute path normalized to the semantics of the underlying file system. id = pathUtil.join.apply(pathUtil, segments); } return id; } }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 | 1 | define([/*===== "./_base/declare", =====*/ "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"], function(/*===== declare, =====*/ lang, i18n, nlsNumber, dstring, dregexp){ // module: // dojo/number var number = { // summary: // localized formatting and parsing routines for Number }; lang.setObject("dojo.number", number); /*===== number.__FormatOptions = declare(null, { // pattern: String? // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // with this string. Default value is based on locale. Overriding this property will defeat // localization. Literal characters in patterns are not supported. // type: String? // choose a format type based on the locale from the following: // decimal, scientific (not yet supported), percent, currency. decimal by default. // places: Number? // fixed number of decimal places to show. This overrides any // information in the provided pattern. // round: Number? // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 // means do not round. // locale: String? // override the locale used to determine formatting rules // fractional: Boolean? // If false, show no decimal places, overriding places and pattern settings. }); =====*/ number.format = function(/*Number*/ value, /*number.__FormatOptions?*/ options){ // summary: // Format a Number as a String, using locale-specific settings // description: // Create a string from a Number using a known localized pattern. // Formatting patterns appropriate to the locale are chosen from the // [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and // delimiters. // If value is Infinity, -Infinity, or is not a valid JavaScript number, return null. // value: // the number to be formatted options = lang.mixin({}, options || {}); var locale = i18n.normalizeLocale(options.locale), bundle = i18n.getLocalization("dojo.cldr", "number", locale); options.customs = bundle; var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"]; if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null return number._applyPattern(value, pattern, options); // String }; //number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough number._applyPattern = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatOptions?*/ options){ // summary: // Apply pattern to format value as a string using options. Gives no // consideration to local customs. // value: // the number to be formatted. // pattern: // a pattern string as described by // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // options: number.__FormatOptions? // _applyPattern is usually called via `dojo/number.format()` which // populates an extra property in the options parameter, "customs". // The customs object specifies group and decimal parameters if set. //TODO: support escapes options = options || {}; var group = options.customs.group, decimal = options.customs.decimal, patternList = pattern.split(';'), positivePattern = patternList[0]; pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern); //TODO: only test against unescaped if(pattern.indexOf('%') != -1){ value *= 100; }else if(pattern.indexOf('\u2030') != -1){ value *= 1000; // per mille }else if(pattern.indexOf('\u00a4') != -1){ group = options.customs.currencyGroup || group;//mixins instead? decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead? pattern = pattern.replace(/\u00a4{1,3}/, function(match){ var prop = ["symbol", "currency", "displayName"][match.length-1]; return options[prop] || options.currency || ""; }); }else if(pattern.indexOf('E') != -1){ throw new Error("exponential notation not supported"); } //TODO: support @ sig figs? var numberPatternRE = number._numberPatternRE; var numberPattern = positivePattern.match(numberPatternRE); if(!numberPattern){ throw new Error("unable to find a number expression in pattern: "+pattern); } if(options.fractional === false){ options.places = 0; } return pattern.replace(numberPatternRE, number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round})); }; number.round = function(/*Number*/ value, /*Number?*/ places, /*Number?*/ increment){ // summary: // Rounds to the nearest value with the given number of decimal places, away from zero // description: // Rounds to the nearest value with the given number of decimal places, away from zero if equal. // Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by // fractional increments also, such as the nearest quarter. // NOTE: Subject to floating point errors. See dojox/math/round for experimental workaround. // value: // The number to round // places: // The number of decimal places where rounding takes place. Defaults to 0 for whole rounding. // Must be non-negative. // increment: // Rounds next place to nearest value of increment/10. 10 by default. // example: // | >>> number.round(-0.5) // | -1 // | >>> number.round(162.295, 2) // | 162.29 // note floating point error. Should be 162.3 // | >>> number.round(10.71, 0, 2.5) // | 10.75 var factor = 10 / (increment || 10); return (factor * +value).toFixed(places) / factor; // Number }; if((0.9).toFixed() == 0){ // (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit // is just after the rounding place and is >=5 var round = number.round; number.round = function(v, p, m){ var d = Math.pow(10, -p || 0), a = Math.abs(v); if(!v || a >= d){ d = 0; }else{ a /= d; if(a < 0.5 || a >= 0.95){ d = 0; } } return round(v, p, m) + (v > 0 ? d : -d); }; // Use "doc hint" so the doc parser ignores this new definition of round(), and uses the one above. /*===== number.round = round; =====*/ } /*===== number.__FormatAbsoluteOptions = declare(null, { // decimal: String? // the decimal separator // group: String? // the group separator // places: Number|String? // number of decimal places. the range "n,m" will format to m places. // round: Number? // 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1 // means don't round. }); =====*/ number._formatAbsolute = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatAbsoluteOptions?*/ options){ // summary: // Apply numeric pattern to absolute value using options. Gives no // consideration to local customs. // value: // the number to be formatted, ignores sign // pattern: // the number portion of a pattern (e.g. `#,##0.00`) options = options || {}; if(options.places === true){options.places=0;} if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit var patternParts = pattern.split("."), comma = typeof options.places == "string" && options.places.indexOf(","), maxPlaces = options.places; if(comma){ maxPlaces = options.places.substring(comma + 1); }else if(!(maxPlaces >= 0)){ maxPlaces = (patternParts[1] || []).length; } if(!(options.round < 0)){ value = number.round(value, maxPlaces, options.round); } var valueParts = String(Math.abs(value)).split("."), fractional = valueParts[1] || ""; if(patternParts[1] || options.places){ if(comma){ options.places = options.places.substring(0, comma); } // Pad fractional with trailing zeros var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1); if(pad > fractional.length){ valueParts[1] = dstring.pad(fractional, pad, '0', true); } // Truncate fractional if(maxPlaces < fractional.length){ valueParts[1] = fractional.substr(0, maxPlaces); } }else{ if(valueParts[1]){ valueParts.pop(); } } // Pad whole with leading zeros var patternDigits = patternParts[0].replace(',', ''); pad = patternDigits.indexOf("0"); if(pad != -1){ pad = patternDigits.length - pad; if(pad > valueParts[0].length){ valueParts[0] = dstring.pad(valueParts[0], pad); } // Truncate whole if(patternDigits.indexOf("#") == -1){ valueParts[0] = valueParts[0].substr(valueParts[0].length - pad); } } // Add group separators var index = patternParts[0].lastIndexOf(','), groupSize, groupSize2; if(index != -1){ groupSize = patternParts[0].length - index - 1; var remainder = patternParts[0].substr(0, index); index = remainder.lastIndexOf(','); if(index != -1){ groupSize2 = remainder.length - index - 1; } } var pieces = []; for(var whole = valueParts[0]; whole;){ var off = whole.length - groupSize; pieces.push((off > 0) ? whole.substr(off) : whole); whole = (off > 0) ? whole.slice(0, off) : ""; if(groupSize2){ groupSize = groupSize2; delete groupSize2; } } valueParts[0] = pieces.reverse().join(options.group || ","); return valueParts.join(options.decimal || "."); }; /*===== number.__RegexpOptions = declare(null, { // pattern: String? // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // with this string. Default value is based on locale. Overriding this property will defeat // localization. // type: String? // choose a format type based on the locale from the following: // decimal, scientific (not yet supported), percent, currency. decimal by default. // locale: String? // override the locale used to determine formatting rules // strict: Boolean? // strict parsing, false by default. Strict parsing requires input as produced by the format() method. // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators // places: Number|String? // number of decimal places to accept: Infinity, a positive number, or // a range "n,m". Defined by pattern or Infinity if pattern not provided. }); =====*/ number.regexp = function(/*number.__RegexpOptions?*/ options){ // summary: // Builds the regular needed to parse a number // description: // Returns regular expression with positive and negative match, group // and decimal separators return number._parseInfo(options).regexp; // String }; number._parseInfo = function(/*Object?*/ options){ options = options || {}; var locale = i18n.normalizeLocale(options.locale), bundle = i18n.getLocalization("dojo.cldr", "number", locale), pattern = options.pattern || bundle[(options.type || "decimal") + "Format"], //TODO: memoize? group = bundle.group, decimal = bundle.decimal, factor = 1; if(pattern.indexOf('%') != -1){ factor /= 100; }else if(pattern.indexOf('\u2030') != -1){ factor /= 1000; // per mille }else{ var isCurrency = pattern.indexOf('\u00a4') != -1; if(isCurrency){ group = bundle.currencyGroup || group; decimal = bundle.currencyDecimal || decimal; } } //TODO: handle quoted escapes var patternList = pattern.split(';'); if(patternList.length == 1){ patternList.push("-" + patternList[0]); } var re = dregexp.buildGroupRE(patternList, function(pattern){ pattern = "(?:"+dregexp.escapeString(pattern, '.')+")"; return pattern.replace(number._numberPatternRE, function(format){ var flags = { signed: false, separator: options.strict ? group : [group,""], fractional: options.fractional, decimal: decimal, exponent: false }, parts = format.split('.'), places = options.places; // special condition for percent (factor != 1) // allow decimal places even if not specified in pattern if(parts.length == 1 && factor != 1){ parts[1] = "###"; } if(parts.length == 1 || places === 0){ flags.fractional = false; }else{ if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; } if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; } flags.places = places; } var groups = parts[0].split(','); if(groups.length > 1){ flags.groupSize = groups.pop().length; if(groups.length > 1){ flags.groupSize2 = groups.pop().length; } } return "("+number._realNumberRegexp(flags)+")"; }); }, true); if(isCurrency){ // substitute the currency symbol for the placeholder in the pattern re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){ var prop = ["symbol", "currency", "displayName"][target.length-1], symbol = dregexp.escapeString(options[prop] || options.currency || ""); before = before ? "[\\s\\xa0]" : ""; after = after ? "[\\s\\xa0]" : ""; if(!options.strict){ if(before){before += "*";} if(after){after += "*";} return "(?:"+before+symbol+after+")?"; } return before+symbol+after; }); } //TODO: substitute localized sign/percent/permille/etc.? // normalize whitespace and return return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object }; /*===== number.__ParseOptions = declare(null, { // pattern: String? // override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // with this string. Default value is based on locale. Overriding this property will defeat // localization. Literal characters in patterns are not supported. // type: String? // choose a format type based on the locale from the following: // decimal, scientific (not yet supported), percent, currency. decimal by default. // locale: String? // override the locale used to determine formatting rules // strict: Boolean? // strict parsing, false by default. Strict parsing requires input as produced by the format() method. // Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators // fractional: Boolean|Array? // Whether to include the fractional portion, where the number of decimal places are implied by pattern // or explicit 'places' parameter. The value [true,false] makes the fractional portion optional. }); =====*/ number.parse = function(/*String*/ expression, /*number.__ParseOptions?*/ options){ // summary: // Convert a properly formatted string to a primitive Number, using // locale-specific settings. // description: // Create a Number from a string using a known localized pattern. // Formatting patterns are chosen appropriate to the locale // and follow the syntax described by // [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns) // Note that literal characters in patterns are not supported. // expression: // A string representation of a Number var info = number._parseInfo(options), results = (new RegExp("^"+info.regexp+"$")).exec(expression); if(!results){ return NaN; //NaN } var absoluteMatch = results[1]; // match for the positive expression if(!results[1]){ if(!results[2]){ return NaN; //NaN } // matched the negative pattern absoluteMatch =results[2]; info.factor *= -1; } // Transform it to something Javascript can parse as a number. Normalize // decimal point and strip out group separators or alternate forms of whitespace absoluteMatch = absoluteMatch. replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), ""). replace(info.decimal, "."); // Adjust for negative sign, percent, etc. as necessary return absoluteMatch * info.factor; //Number }; /*===== number.__RealNumberRegexpFlags = declare(null, { // places: Number? // The integer number of decimal places or a range given as "n,m". If // not given, the decimal part is optional and the number of places is // unlimited. // decimal: String? // A string for the character used as the decimal point. Default // is ".". // fractional: Boolean|Array? // Whether decimal places are used. Can be true, false, or [true, // false]. Default is [true, false] which means optional. // exponent: Boolean|Array? // Express in exponential notation. Can be true, false, or [true, // false]. Default is [true, false], (i.e. will match if the // exponential part is present are not). // eSigned: Boolean|Array? // The leading plus-or-minus sign on the exponent. Can be true, // false, or [true, false]. Default is [true, false], (i.e. will // match if it is signed or unsigned). flags in regexp.integer can be // applied. }); =====*/ number._realNumberRegexp = function(/*__RealNumberRegexpFlags?*/ flags){ // summary: // Builds a regular expression to match a real number in exponential // notation // assign default values to missing parameters flags = flags || {}; //TODO: use mixin instead? if(!("places" in flags)){ flags.places = Infinity; } if(typeof flags.decimal != "string"){ flags.decimal = "."; } if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; } if(!("exponent" in flags)){ flags.exponent = [true, false]; } if(!("eSigned" in flags)){ flags.eSigned = [true, false]; } var integerRE = number._integerRegexp(flags), decimalRE = dregexp.buildGroupRE(flags.fractional, function(q){ var re = ""; if(q && (flags.places!==0)){ re = "\\" + flags.decimal; if(flags.places == Infinity){ re = "(?:" + re + "\\d+)?"; }else{ re += "\\d{" + flags.places + "}"; } } return re; }, true ); var exponentRE = dregexp.buildGroupRE(flags.exponent, function(q){ if(q){ return "([eE]" + number._integerRegexp({ signed: flags.eSigned}) + ")"; } return ""; } ); var realRE = integerRE + decimalRE; // allow for decimals without integers, e.g. .25 if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";} return realRE + exponentRE; // String }; /*===== number.__IntegerRegexpFlags = declare(null, { // signed: Boolean? // The leading plus-or-minus sign. Can be true, false, or `[true,false]`. // Default is `[true, false]`, (i.e. will match if it is signed // or unsigned). // separator: String? // The character used as the thousands separator. Default is no // separator. For more than one symbol use an array, e.g. `[",", ""]`, // makes ',' optional. // groupSize: Number? // group size between separators // groupSize2: Number? // second grouping, where separators 2..n have a different interval than the first separator (for India) }); =====*/ number._integerRegexp = function(/*number.__IntegerRegexpFlags?*/ flags){ // summary: // Builds a regular expression that matches an integer // assign default values to missing parameters flags = flags || {}; if(!("signed" in flags)){ flags.signed = [true, false]; } if(!("separator" in flags)){ flags.separator = ""; }else if(!("groupSize" in flags)){ flags.groupSize = 3; } var signRE = dregexp.buildGroupRE(flags.signed, function(q){ return q ? "[-+]" : ""; }, true ); var numberRE = dregexp.buildGroupRE(flags.separator, function(sep){ if(!sep){ return "(?:\\d+)"; } sep = dregexp.escapeString(sep); if(sep == " "){ sep = "\\s"; } else if(sep == "\xa0"){ sep = "\\s\\xa0"; } var grp = flags.groupSize, grp2 = flags.groupSize2; //TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933 if(grp2){ var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})"; return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE; } return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)"; }, true ); return signRE + numberRE; // String }; return number; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | 1 1 1 1 1 | define(["./has!dom-addeventlistener?:./aspect", "./_base/kernel", "./sniff"], function(aspect, dojo, has){ "use strict"; if(has("dom")){ // check to make sure we are in a browser, this module should work anywhere var major = window.ScriptEngineMajorVersion; has.add("jscript", major && (major() + ScriptEngineMinorVersion() / 10)); has.add("event-orientationchange", has("touch") && !has("android")); // TODO: how do we detect this? has.add("event-stopimmediatepropagation", window.Event && !!window.Event.prototype && !!window.Event.prototype.stopImmediatePropagation); has.add("event-focusin", function(global, doc, element){ return 'onfocusin' in element; }); } var on = function(target, type, listener, dontFix){ // summary: // A function that provides core event listening functionality. With this function // you can provide a target, event type, and listener to be notified of // future matching events that are fired. // target: Element|Object // This is the target object or DOM element that to receive events from // type: String|Function // This is the name of the event to listen for or an extension event type. // listener: Function // This is the function that should be called when the event fires. // returns: Object // An object with a remove() method that can be used to stop listening for this // event. // description: // To listen for "click" events on a button node, we can do: // | define(["dojo/on"], function(listen){ // | on(button, "click", clickHandler); // | ... // Evented JavaScript objects can also have their own events. // | var obj = new Evented; // | on(obj, "foo", fooHandler); // And then we could publish a "foo" event: // | on.emit(obj, "foo", {key: "value"}); // We can use extension events as well. For example, you could listen for a tap gesture: // | define(["dojo/on", "dojo/gesture/tap", function(listen, tap){ // | on(button, tap, tapHandler); // | ... // which would trigger fooHandler. Note that for a simple object this is equivalent to calling: // | obj.onfoo({key:"value"}); // If you use on.emit on a DOM node, it will use native event dispatching when possible. if(typeof target.on == "function" && typeof type != "function" && !target.nodeType){ // delegate to the target's on() method, so it can handle it's own listening if it wants (unless it // is DOM node and we may be dealing with jQuery or Prototype's incompatible addition to the // Element prototype return target.on(type, listener); } // delegate to main listener code return on.parse(target, type, listener, addListener, dontFix, this); }; on.pausable = function(target, type, listener, dontFix){ // summary: // This function acts the same as on(), but with pausable functionality. The // returned signal object has pause() and resume() functions. Calling the // pause() method will cause the listener to not be called for future events. Calling the // resume() method will cause the listener to again be called for future events. var paused; var signal = on(target, type, function(){ if(!paused){ return listener.apply(this, arguments); } }, dontFix); signal.pause = function(){ paused = true; }; signal.resume = function(){ paused = false; }; return signal; }; on.once = function(target, type, listener, dontFix){ // summary: // This function acts the same as on(), but will only call the listener once. The // listener will be called for the first // event that takes place and then listener will automatically be removed. var signal = on(target, type, function(){ // remove this listener signal.remove(); // proceed to call the listener return listener.apply(this, arguments); }); return signal; }; on.parse = function(target, type, listener, addListener, dontFix, matchesTarget){ if(type.call){ // event handler function // on(node, touch.press, touchListener); return type.call(matchesTarget, target, listener); } if(type.indexOf(",") > -1){ // we allow comma delimited event names, so you can register for multiple events at once var events = type.split(/\s*,\s*/); var handles = []; var i = 0; var eventName; while(eventName = events[i++]){ handles.push(addListener(target, eventName, listener, dontFix, matchesTarget)); } handles.remove = function(){ for(var i = 0; i < handles.length; i++){ handles[i].remove(); } }; return handles; } return addListener(target, type, listener, dontFix, matchesTarget); }; var touchEvents = /^touch/; function addListener(target, type, listener, dontFix, matchesTarget){ // event delegation: var selector = type.match(/(.*):(.*)/); // if we have a selector:event, the last one is interpreted as an event, and we use event delegation if(selector){ type = selector[2]; selector = selector[1]; // create the extension event for selectors and directly call it return on.selector(selector, type).call(matchesTarget, target, listener); } // test to see if it a touch event right now, so we don't have to do it every time it fires if(has("touch")){ if(touchEvents.test(type)){ // touch event, fix it listener = fixTouchListener(listener); } if(!has("event-orientationchange") && (type == "orientationchange")){ //"orientationchange" not supported <= Android 2.1, //but works through "resize" on window type = "resize"; target = window; listener = fixTouchListener(listener); } } if(addStopImmediate){ // add stopImmediatePropagation if it doesn't exist listener = addStopImmediate(listener); } // normal path, the target is |this| if(target.addEventListener){ // the target has addEventListener, which should be used if available (might or might not be a node, non-nodes can implement this method as well) // check for capture conversions var capture = type in captures, adjustedType = capture ? captures[type] : type; target.addEventListener(adjustedType, listener, capture); // create and return the signal return { remove: function(){ target.removeEventListener(adjustedType, listener, capture); } }; } type = "on" + type; if(fixAttach && target.attachEvent){ return fixAttach(target, type, listener); } throw new Error("Target must be an event emitter"); } on.selector = function(selector, eventType, children){ // summary: // Creates a new extension event with event delegation. This is based on // the provided event type (can be extension event) that // only calls the listener when the CSS selector matches the target of the event. // // The application must require() an appropriate level of dojo/query to handle the selector. // selector: // The CSS selector to use for filter events and determine the |this| of the event listener. // eventType: // The event to listen for // children: // Indicates if children elements of the selector should be allowed. This defaults to // true // example: // | require(["dojo/on", "dojo/mouse", "dojo/query!css2"], function(listen, mouse){ // | on(node, on.selector(".my-class", mouse.enter), handlerForMyHover); return function(target, listener){ // if the selector is function, use it to select the node, otherwise use the matches method var matchesTarget = typeof selector == "function" ? {matches: selector} : this, bubble = eventType.bubble; function select(eventTarget){ // see if we have a valid matchesTarget or default to dojo/query matchesTarget = matchesTarget && matchesTarget.matches ? matchesTarget : dojo.query; // there is a selector, so make sure it matches while(!matchesTarget.matches(eventTarget, selector, target)){ if(eventTarget == target || children === false || !(eventTarget = eventTarget.parentNode) || eventTarget.nodeType != 1){ // intentional assignment return; } } return eventTarget; } if(bubble){ // the event type doesn't naturally bubble, but has a bubbling form, use that, and give it the selector so it can perform the select itself return on(target, bubble(select), listener); } // standard event delegation return on(target, eventType, function(event){ // call select to see if we match var eventTarget = select(event.target); // if it matches we call the listener return eventTarget && listener.call(eventTarget, event); }); }; }; function syntheticPreventDefault(){ this.cancelable = false; this.defaultPrevented = true; } function syntheticStopPropagation(){ this.bubbles = false; } var slice = [].slice, syntheticDispatch = on.emit = function(target, type, event){ // summary: // Fires an event on the target object. // target: // The target object to fire the event on. This can be a DOM element or a plain // JS object. If the target is a DOM element, native event emitting mechanisms // are used when possible. // type: // The event type name. You can emulate standard native events like "click" and // "mouseover" or create custom events like "open" or "finish". // event: // An object that provides the properties for the event. See https://developer.mozilla.org/en/DOM/event.initEvent // for some of the properties. These properties are copied to the event object. // Of particular importance are the cancelable and bubbles properties. The // cancelable property indicates whether or not the event has a default action // that can be cancelled. The event is cancelled by calling preventDefault() on // the event object. The bubbles property indicates whether or not the // event will bubble up the DOM tree. If bubbles is true, the event will be called // on the target and then each parent successively until the top of the tree // is reached or stopPropagation() is called. Both bubbles and cancelable // default to false. // returns: // If the event is cancelable and the event is not cancelled, // emit will return true. If the event is cancelable and the event is cancelled, // emit will return false. // details: // Note that this is designed to emit events for listeners registered through // dojo/on. It should actually work with any event listener except those // added through IE's attachEvent (IE8 and below's non-W3C event emitting // doesn't support custom event types). It should work with all events registered // through dojo/on. Also note that the emit method does do any default // action, it only returns a value to indicate if the default action should take // place. For example, emitting a keypress event would not cause a character // to appear in a textbox. // example: // To fire our own click event // | require(["dojo/on", "dojo/dom" // | ], function(on, dom){ // | on.emit(dom.byId("button"), "click", { // | cancelable: true, // | bubbles: true, // | screenX: 33, // | screenY: 44 // | }); // We can also fire our own custom events: // | on.emit(dom.byId("slider"), "slide", { // | cancelable: true, // | bubbles: true, // | direction: "left-to-right" // | }); // | }); var args = slice.call(arguments, 2); var method = "on" + type; if("parentNode" in target){ // node (or node-like), create event controller methods var newEvent = args[0] = {}; for(var i in event){ newEvent[i] = event[i]; } newEvent.preventDefault = syntheticPreventDefault; newEvent.stopPropagation = syntheticStopPropagation; newEvent.target = target; newEvent.type = type; event = newEvent; } do{ // call any node which has a handler (note that ideally we would try/catch to simulate normal event propagation but that causes too much pain for debugging) target[method] && target[method].apply(target, args); // and then continue up the parent node chain if it is still bubbling (if started as bubbles and stopPropagation hasn't been called) }while(event && event.bubbles && (target = target.parentNode)); return event && event.cancelable && event; // if it is still true (was cancelable and was cancelled), return the event to indicate default action should happen }; var captures = has("event-focusin") ? {} : {focusin: "focus", focusout: "blur"}; if(!has("event-stopimmediatepropagation")){ var stopImmediatePropagation =function(){ this.immediatelyStopped = true; this.modified = true; // mark it as modified so the event will be cached in IE }; var addStopImmediate = function(listener){ return function(event){ if(!event.immediatelyStopped){// check to make sure it hasn't been stopped immediately event.stopImmediatePropagation = stopImmediatePropagation; return listener.apply(this, arguments); } }; } } if(has("dom-addeventlistener")){ // emitter that works with native event handling on.emit = function(target, type, event){ if(target.dispatchEvent && document.createEvent){ // use the native event emitting mechanism if it is available on the target object // create a generic event // we could create branch into the different types of event constructors, but // that would be a lot of extra code, with little benefit that I can see, seems // best to use the generic constructor and copy properties over, making it // easy to have events look like the ones created with specific initializers var ownerDocument = target.ownerDocument || document; var nativeEvent = ownerDocument.createEvent("HTMLEvents"); nativeEvent.initEvent(type, !!event.bubbles, !!event.cancelable); // and copy all our properties over for(var i in event){ if(!(i in nativeEvent)){ nativeEvent[i] = event[i]; } } return target.dispatchEvent(nativeEvent) && nativeEvent; } return syntheticDispatch.apply(on, arguments); // emit for a non-node }; }else{ // no addEventListener, basically old IE event normalization on._fixEvent = function(evt, sender){ // summary: // normalizes properties on the event object including event // bubbling methods, keystroke normalization, and x/y positions // evt: // native event object // sender: // node to treat as "currentTarget" if(!evt){ var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window; evt = w.event; } if(!evt){return evt;} try{ if(lastEvent && evt.type == lastEvent.type && evt.srcElement == lastEvent.target){ // should be same event, reuse event object (so it can be augmented); // accessing evt.srcElement rather than evt.target since evt.target not set on IE until fixup below evt = lastEvent; } }catch(e){ // will occur on IE on lastEvent.type reference if lastEvent points to a previous event that already // finished bubbling, but the setTimeout() to clear lastEvent hasn't fired yet } if(!evt.target){ // check to see if it has been fixed yet evt.target = evt.srcElement; evt.currentTarget = (sender || evt.srcElement); if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; } if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; } if(!evt.stopPropagation){ evt.stopPropagation = stopPropagation; evt.preventDefault = preventDefault; } switch(evt.type){ case "keypress": var c = ("charCode" in evt ? evt.charCode : evt.keyCode); if (c==10){ // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla c=0; evt.keyCode = 13; }else if(c==13||c==27){ c=0; // Mozilla considers ENTER and ESC non-printable }else if(c==3){ c=99; // Mozilla maps CTRL-BREAK to CTRL-c } // Mozilla sets keyCode to 0 when there is a charCode // but that stops the event on IE. evt.charCode = c; _setKeyChar(evt); break; } } return evt; }; var lastEvent, IESignal = function(handle){ this.handle = handle; }; IESignal.prototype.remove = function(){ delete _dojoIEListeners_[this.handle]; }; var fixListener = function(listener){ // this is a minimal function for closing on the previous listener with as few as variables as possible return function(evt){ evt = on._fixEvent(evt, this); var result = listener.call(this, evt); if(evt.modified){ // cache the last event and reuse it if we can if(!lastEvent){ setTimeout(function(){ lastEvent = null; }); } lastEvent = evt; } return result; }; }; var fixAttach = function(target, type, listener){ listener = fixListener(listener); if(((target.ownerDocument ? target.ownerDocument.parentWindow : target.parentWindow || target.window || window) != top || has("jscript") < 5.8) && !has("config-_allow_leaks")){ // IE will leak memory on certain handlers in frames (IE8 and earlier) and in unattached DOM nodes for JScript 5.7 and below. // Here we use global redirection to solve the memory leaks if(typeof _dojoIEListeners_ == "undefined"){ _dojoIEListeners_ = []; } var emitter = target[type]; if(!emitter || !emitter.listeners){ var oldListener = emitter; emitter = Function('event', 'var callee = arguments.callee; for(var i = 0; i<callee.listeners.length; i++){var listener = _dojoIEListeners_[callee.listeners[i]]; if(listener){listener.call(this,event);}}'); emitter.listeners = []; target[type] = emitter; emitter.global = this; if(oldListener){ emitter.listeners.push(_dojoIEListeners_.push(oldListener) - 1); } } var handle; emitter.listeners.push(handle = (emitter.global._dojoIEListeners_.push(listener) - 1)); return new IESignal(handle); } return aspect.after(target, type, listener, true); }; var _setKeyChar = function(evt){ evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : ''; evt.charOrCode = evt.keyChar || evt.keyCode; }; // Called in Event scope var stopPropagation = function(){ this.cancelBubble = true; }; var preventDefault = on._preventDefault = function(){ // Setting keyCode to 0 is the only way to prevent certain keypresses (namely // ctrl-combinations that correspond to menu accelerator keys). // Otoh, it prevents upstream listeners from getting this information // Try to split the difference here by clobbering keyCode only for ctrl // combinations. If you still need to access the key upstream, bubbledKeyCode is // provided as a workaround. this.bubbledKeyCode = this.keyCode; if(this.ctrlKey){ try{ // squelch errors when keyCode is read-only // (e.g. if keyCode is ctrl or shift) this.keyCode = 0; }catch(e){ } } this.defaultPrevented = true; this.returnValue = false; this.modified = true; // mark it as modified (for defaultPrevented flag) so the event will be cached in IE }; } if(has("touch")){ var Event = function(){}; var windowOrientation = window.orientation; var fixTouchListener = function(listener){ return function(originalEvent){ //Event normalization(for ontouchxxx and resize): //1.incorrect e.pageX|pageY in iOS //2.there are no "e.rotation", "e.scale" and "onorientationchange" in Android //3.More TBD e.g. force | screenX | screenX | clientX | clientY | radiusX | radiusY // see if it has already been corrected var event = originalEvent.corrected; if(!event){ var type = originalEvent.type; try{ delete originalEvent.type; // on some JS engines (android), deleting properties make them mutable }catch(e){} if(originalEvent.type){ // deleting properties doesn't work (older iOS), have to use delegation if(has('mozilla')){ // Firefox doesn't like delegated properties, so we have to copy var event = {}; for(var name in originalEvent){ event[name] = originalEvent[name]; } }else{ // old iOS branch Event.prototype = originalEvent; var event = new Event; } // have to delegate methods to make them work event.preventDefault = function(){ originalEvent.preventDefault(); }; event.stopPropagation = function(){ originalEvent.stopPropagation(); }; }else{ // deletion worked, use property as is event = originalEvent; event.type = type; } originalEvent.corrected = event; if(type == 'resize'){ if(windowOrientation == window.orientation){ return null;//double tap causes an unexpected 'resize' in Android } windowOrientation = window.orientation; event.type = "orientationchange"; return listener.call(this, event); } // We use the original event and augment, rather than doing an expensive mixin operation if(!("rotation" in event)){ // test to see if it has rotation event.rotation = 0; event.scale = 1; } //use event.changedTouches[0].pageX|pageY|screenX|screenY|clientX|clientY|target var firstChangeTouch = event.changedTouches[0]; for(var i in firstChangeTouch){ // use for-in, we don't need to have dependency on dojo/_base/lang here delete event[i]; // delete it first to make it mutable event[i] = firstChangeTouch[i]; } } return listener.call(this, event); }; }; } return on; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 | 1 1 1 1 1 1 1 1 | define([ "require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./dom", "./_base/window", "./_base/url", "./aspect", "./promise/all", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready" ], function(require, dojo, dlang, darray, config, dom, dwindow, _Url, aspect, all, dates, Deferred, has, query, don, ready){ // module: // dojo/parser new Date("X"); // workaround for #11279, new Date("") == NaN // data-dojo-props etc. is not restricted to JSON, it can be any javascript function myEval(text){ return eval("(" + text + ")"); } // Widgets like BorderContainer add properties to _Widget via dojo.extend(). // If BorderContainer is loaded after _Widget's parameter list has been cached, // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget). var extendCnt = 0; aspect.after(dlang, "extend", function(){ extendCnt++; }, true); function getNameMap(ctor){ // summary: // Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"} var map = ctor._nameCaseMap, proto = ctor.prototype; // Create the map if it's undefined. // Refresh the map if a superclass was possibly extended with new methods since the map was created. if(!map || map._extendCnt < extendCnt){ map = ctor._nameCaseMap = {}; for(var name in proto){ if(name.charAt(0) === "_"){ continue; } // skip internal properties map[name.toLowerCase()] = name; } map._extendCnt = extendCnt; } return map; } // Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor. var _ctorMap = {}; function getCtor(/*String[]*/ types, /*Function?*/ contextRequire){ // summary: // Retrieves a constructor. If the types array contains more than one class/MID then the // subsequent classes will be mixed into the first class and a unique constructor will be // returned for that array. var ts = types.join(); if(!_ctorMap[ts]){ var mixins = []; for(var i = 0, l = types.length; i < l; i++){ var t = types[i]; // TODO: Consider swapping getObject and require in the future mixins[mixins.length] = (_ctorMap[t] = _ctorMap[t] || (dlang.getObject(t) || (~t.indexOf('/') && (contextRequire ? contextRequire(t) : require(t))))); } var ctor = mixins.shift(); _ctorMap[ts] = mixins.length ? (ctor.createSubclass ? ctor.createSubclass(mixins) : ctor.extend.apply(ctor, mixins)) : ctor; } return _ctorMap[ts]; } var parser = { // summary: // The Dom/Widget parsing package _clearCache: function(){ // summary: // Clear cached data. Used mainly for benchmarking. extendCnt++; _ctorMap = {}; }, _functionFromScript: function(script, attrData){ // summary: // Convert a `<script type="dojo/method" args="a, b, c"> ... </script>` // into a function // script: DOMNode // The `<script>` DOMNode // attrData: String // For HTML5 compliance, searches for attrData + "args" (typically // "data-dojo-args") instead of "args" var preamble = "", suffix = "", argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")), withStr = script.getAttribute("with"); // Convert any arguments supplied in script tag into an array to be passed to the var fnArgs = (argsStr || "").split(/\s*,\s*/); if(withStr && withStr.length){ darray.forEach(withStr.split(/\s*,\s*/), function(part){ preamble += "with(" + part + "){"; suffix += "}"; }); } return new Function(fnArgs, preamble + script.innerHTML + suffix); }, instantiate: function(nodes, mixin, options){ // summary: // Takes array of nodes, and turns them into class instances and // potentially calls a startup method to allow them to connect with // any children. // nodes: Array // Array of DOM nodes // mixin: Object? // An object that will be mixed in with each node in the array. // Values in the mixin will override values in the node, if they // exist. // options: Object? // An object used to hold kwArgs for instantiation. // See parse.options argument for details. // returns: // Array of instances. mixin = mixin || {}; options = options || {}; var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType" attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-" dataDojoType = attrData + "type", // typically "data-dojo-type" dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins" var list = []; darray.forEach(nodes, function(node){ var type = dojoType in mixin ? mixin[dojoType] : node.getAttribute(dataDojoType) || node.getAttribute(dojoType); if(type){ var mixinsValue = node.getAttribute(dataDojoMixins), types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type]; list.push({ node: node, types: types }); } }); // Instantiate the nodes and return the list of instances. return this._instantiate(list, mixin, options); }, _instantiate: function(nodes, mixin, options, returnPromise){ // summary: // Takes array of objects representing nodes, and turns them into class instances and // potentially calls a startup method to allow them to connect with // any children. // nodes: Array // Array of objects like // | { // | ctor: Function (may be null) // | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified) // | node: DOMNode, // | scripts: [ ... ], // array of <script type="dojo/..."> children of node // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc. // | } // mixin: Object // An object that will be mixed in with each node in the array. // Values in the mixin will override values in the node, if they // exist. // options: Object // An options object used to hold kwArgs for instantiation. // See parse.options argument for details. // returnPromise: Boolean // Return a Promise rather than the instance; supports asynchronous widget creation. // returns: // Array of instances, or if returnPromise is true, a promise for array of instances // that resolves when instances have finished initializing. // Call widget constructors. Some may be asynchronous and return promises. var thelist = darray.map(nodes, function(obj){ var ctor = obj.ctor || getCtor(obj.types, options.contextRequire); // If we still haven't resolved a ctor, it is fatal now if(!ctor){ throw new Error("Unable to resolve constructor for: '" + obj.types.join() + "'"); } return this.construct(ctor, obj.node, mixin, options, obj.scripts, obj.inherited); }, this); // After all widget construction finishes, call startup on each top level instance if it makes sense (as for // widgets). Parent widgets will recursively call startup on their (non-top level) children function onConstruct(thelist){ if(!mixin._started && !options.noStart){ darray.forEach(thelist, function(instance){ if(typeof instance.startup === "function" && !instance._started){ instance.startup(); } }); } return thelist; } if(returnPromise){ return all(thelist).then(onConstruct); }else{ // Back-compat path, remove for 2.0 return onConstruct(thelist); } }, construct: function(ctor, node, mixin, options, scripts, inherited){ // summary: // Calls new ctor(params, node), where params is the hash of parameters specified on the node, // excluding data-dojo-type and data-dojo-mixins. Does not call startup(). // ctor: Function // Widget constructor. // node: DOMNode // This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor. // mixin: Object? // Attributes in this object will be passed as parameters to ctor, // overriding attributes specified on the node. // options: Object? // An options object used to hold kwArgs for instantiation. See parse.options argument for details. // scripts: DomNode[]? // Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node. // inherited: Object? // Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited. // returns: // Instance or Promise for the instance, if markupFactory() itself returned a promise var proto = ctor && ctor.prototype; options = options || {}; // Setup hash to hold parameter settings for this widget. Start with the parameter // settings inherited from ancestors ("dir" and "lang"). // Inherited setting may later be overridden by explicit settings on node itself. var params = {}; if(options.defaults){ // settings for the document itself (or whatever subtree is being parsed) dlang.mixin(params, options.defaults); } if(inherited){ // settings from dir=rtl or lang=... on a node above this node dlang.mixin(params, inherited); } // Get list of attributes explicitly listed in the markup var attributes; if(has("dom-attributes-explicit")){ // Standard path to get list of user specified attributes attributes = node.attributes; }else if(has("dom-attributes-specified-flag")){ // Special processing needed for IE8, to skip a few faux values in attributes[] attributes = darray.filter(node.attributes, function(a){ return a.specified; }); }else{ // Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false), attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, ""); attributes = darray.map(attrs.split(/\s+/), function(name){ var lcName = name.toLowerCase(); return { name: name, // getAttribute() doesn't work for button.value, returns innerHTML of button. // but getAttributeNode().value doesn't work for the form.encType or li.value value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ? node.getAttribute(lcName) : node.getAttributeNode(lcName).value }; }); } // Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params) // TODO: remove scope for 2.0 var scope = options.scope || dojo._scopeName, attrData = "data-" + scope + "-", // typically "data-dojo-" hash = {}; if(scope !== "dojo"){ hash[attrData + "props"] = "data-dojo-props"; hash[attrData + "type"] = "data-dojo-type"; hash[attrData + "mixins"] = "data-dojo-mixins"; hash[scope + "type"] = "dojoType"; hash[attrData + "id"] = "data-dojo-id"; } // Read in attributes and process them, including data-dojo-props, data-dojo-type, // dojoAttachPoint, etc., as well as normal foo=bar attributes. var i = 0, item, funcAttrs = [], jsname, extra; while(item = attributes[i++]){ var name = item.name, lcName = name.toLowerCase(), value = item.value; switch(hash[lcName] || lcName){ // Already processed, just ignore case "data-dojo-type": case "dojotype": case "data-dojo-mixins": break; // Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings case "data-dojo-props": extra = value; break; // data-dojo-id or jsId. TODO: drop jsId in 2.0 case "data-dojo-id": case "jsid": jsname = value; break; // For the benefit of _Templated case "data-dojo-attach-point": case "dojoattachpoint": params.dojoAttachPoint = value; break; case "data-dojo-attach-event": case "dojoattachevent": params.dojoAttachEvent = value; break; // Special parameter handling needed for IE case "class": params["class"] = node.className; break; case "style": params["style"] = node.style && node.style.cssText; break; default: // Normal attribute, ex: value="123" // Find attribute in widget corresponding to specified name. // May involve case conversion, ex: onclick --> onClick if(!(name in proto)){ var map = getNameMap(ctor); name = map[lcName] || name; } // Set params[name] to value, doing type conversion if(name in proto){ switch(typeof proto[name]){ case "string": params[name] = value; break; case "number": params[name] = value.length ? Number(value) : NaN; break; case "boolean": // for checked/disabled value might be "" or "checked". interpret as true. params[name] = value.toLowerCase() != "false"; break; case "function": if(value === "" || value.search(/[^\w\.]+/i) != -1){ // The user has specified some text for a function like "return x+5" params[name] = new Function(value); }else{ // The user has specified the name of a global function like "myOnClick" // or a single word function "return" params[name] = dlang.getObject(value, false) || new Function(value); } funcAttrs.push(name); // prevent "double connect", see #15026 break; default: var pVal = proto[name]; params[name] = (pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array (pVal instanceof Date) ? (value == "" ? new Date("") : // the NaN of dates value == "now" ? new Date() : // current date dates.fromISOString(value)) : (pVal instanceof _Url) ? (dojo.baseUrl + value) : myEval(value); } }else{ params[name] = value; } } } // Remove function attributes from DOMNode to prevent "double connect" problem, see #15026. // Do this as a separate loop since attributes[] is often a live collection (depends on the browser though). for(var j = 0; j < funcAttrs.length; j++){ var lcfname = funcAttrs[j].toLowerCase(); node.removeAttribute(lcfname); node[lcfname] = null; } // Mix things found in data-dojo-props into the params, overriding any direct settings if(extra){ try{ extra = myEval.call(options.propsThis, "{" + extra + "}"); dlang.mixin(params, extra); }catch(e){ // give the user a pointer to their invalid parameters. FIXME: can we kill this in production? throw new Error(e.toString() + " in data-dojo-props='" + extra + "'"); } } // Any parameters specified in "mixin" override everything else. dlang.mixin(params, mixin); // Get <script> nodes associated with this widget, if they weren't specified explicitly if(!scripts){ scripts = (ctor && (ctor._noScript || proto._noScript) ? [] : query("> script[type^='dojo/']", node)); } // Process <script type="dojo/*"> script tags // <script type="dojo/method" data-dojo-event="foo"> tags are added to params, and passed to // the widget on instantiation. // <script type="dojo/method"> tags (with no event) are executed after instantiation // <script type="dojo/connect" data-dojo-event="foo"> tags are dojo.connected after instantiation, // and likewise with <script type="dojo/aspect" data-dojo-method="foo"> // <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation // <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation // note: dojo/* script tags cannot exist in self closing widgets, like <input /> var aspects = [], // aspects to connect after instantiation calls = [], // functions to call after instantiation watches = [], // functions to watch after instantiation ons = []; // functions to on after instantiation if(scripts){ for(i = 0; i < scripts.length; i++){ var script = scripts[i]; node.removeChild(script); // FIXME: drop event="" support in 2.0. use data-dojo-event="" instead var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")), prop = script.getAttribute(attrData + "prop"), method = script.getAttribute(attrData + "method"), advice = script.getAttribute(attrData + "advice"), scriptType = script.getAttribute("type"), nf = this._functionFromScript(script, attrData); if(event){ if(scriptType == "dojo/connect"){ aspects.push({ method: event, func: nf }); }else if(scriptType == "dojo/on"){ ons.push({ event: event, func: nf }); }else{ // <script type="dojo/method" data-dojo-event="foo"> // TODO for 2.0: use data-dojo-method="foo" instead (also affects dijit/Declaration) params[event] = nf; } }else if(scriptType == "dojo/aspect"){ aspects.push({ method: method, advice: advice, func: nf }); }else if(scriptType == "dojo/watch"){ watches.push({ prop: prop, func: nf }); }else{ calls.push(nf); } } } // create the instance var markupFactory = ctor.markupFactory || proto.markupFactory; var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node); function onInstantiate(instance){ // map it to the JS namespace if that makes sense if(jsname){ dlang.setObject(jsname, instance); } // process connections and startup functions for(i = 0; i < aspects.length; i++){ aspect[aspects[i].advice || "after"](instance, aspects[i].method, dlang.hitch(instance, aspects[i].func), true); } for(i = 0; i < calls.length; i++){ calls[i].call(instance); } for(i = 0; i < watches.length; i++){ instance.watch(watches[i].prop, watches[i].func); } for(i = 0; i < ons.length; i++){ don(instance, ons[i].event, ons[i].func); } return instance; } if(instance.then){ return instance.then(onInstantiate); }else{ return onInstantiate(instance); } }, scan: function(root, options){ // summary: // Scan a DOM tree and return an array of objects representing the DOMNodes // that need to be turned into widgets. // description: // Search specified node (or document root node) recursively for class instances // and return an array of objects that represent potential widgets to be // instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where // "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name // like "dijit/form/Button". If the MID is not currently available, scan will // attempt to require() in the module. // // See parser.parse() for details of markup. // root: DomNode? // A default starting root node from which to start the parsing. Can be // omitted, defaulting to the entire document. If omitted, the `options` // object can be passed in this place. If the `options` object has a // `rootNode` member, that is used. // options: Object // a kwArgs options object, see parse() for details // // returns: Promise // A promise that is resolved with the nodes that have been parsed. var list = [], // Output List mids = [], // An array of modules that are not yet loaded midsHash = {}; // Used to keep the mids array unique var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType" attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-" dataDojoType = attrData + "type", // typically "data-dojo-type" dataDojoTextDir = attrData + "textdir", // typically "data-dojo-textdir" dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins" // Info on DOMNode currently being processed var node = root.firstChild; // Info on parent of DOMNode currently being processed // - inherited: dir, lang, and textDir setting of parent, or inherited by parent // - parent: pointer to identical structure for my parent (or null if no parent) // - scripts: if specified, collects <script type="dojo/..."> type nodes from children var inherited = options.inherited; if(!inherited){ function findAncestorAttr(node, attr){ return (node.getAttribute && node.getAttribute(attr)) || (node.parentNode && findAncestorAttr(node.parentNode, attr)); } inherited = { dir: findAncestorAttr(root, "dir"), lang: findAncestorAttr(root, "lang"), textDir: findAncestorAttr(root, dataDojoTextDir) }; for(var key in inherited){ if(!inherited[key]){ delete inherited[key]; } } } // Metadata about parent node var parent = { inherited: inherited }; // For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect) var scripts; // when true, only look for <script type="dojo/..."> tags, and don't recurse to children var scriptsOnly; function getEffective(parent){ // summary: // Get effective dir, lang, textDir settings for specified obj // (matching "parent" object structure above), and do caching. // Take care not to return null entries. if(!parent.inherited){ parent.inherited = {}; var node = parent.node, grandparent = getEffective(parent.parent); var inherited = { dir: node.getAttribute("dir") || grandparent.dir, lang: node.getAttribute("lang") || grandparent.lang, textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir }; for(var key in inherited){ if(inherited[key]){ parent.inherited[key] = inherited[key]; } } } return parent.inherited; } // DFS on DOM tree, collecting nodes with data-dojo-type specified. while(true){ if(!node){ // Finished this level, continue to parent's next sibling if(!parent || !parent.node){ break; } node = parent.node.nextSibling; scriptsOnly = false; parent = parent.parent; scripts = parent.scripts; continue; } if(node.nodeType != 1){ // Text or comment node, skip to next sibling node = node.nextSibling; continue; } if(scripts && node.nodeName.toLowerCase() == "script"){ // Save <script type="dojo/..."> for parent, then continue to next sibling type = node.getAttribute("type"); if(type && /^dojo\/\w/i.test(type)){ scripts.push(node); } node = node.nextSibling; continue; } if(scriptsOnly){ // scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't // continue further analysis of the node and will continue to the next sibling node = node.nextSibling; continue; } // Check for data-dojo-type attribute, fallback to backward compatible dojoType // TODO: Remove dojoType in 2.0 var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType); // Short circuit for leaf nodes containing nothing [but text] var firstChild = node.firstChild; if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){ node = node.nextSibling; continue; } // Meta data about current node var current; var ctor = null; if(type){ // If dojoType/data-dojo-type specified, add to output array of nodes to instantiate. var mixinsValue = node.getAttribute(dataDojoMixins), types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type]; // Note: won't find classes declared via dojo/Declaration or any modules that haven't been // loaded yet so use try/catch to avoid throw from require() try{ ctor = getCtor(types, options.contextRequire); }catch(e){} // If the constructor was not found, check to see if it has modules that can be loaded if(!ctor){ darray.forEach(types, function(t){ if(~t.indexOf('/') && !midsHash[t]){ // If the type looks like a MID and it currently isn't in the array of MIDs to load, add it. midsHash[t] = true; mids[mids.length] = t; } }); } var childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children // Setup meta data about this widget node, and save it to list of nodes to instantiate current = { types: types, ctor: ctor, parent: parent, node: node, scripts: childScripts }; current.inherited = getEffective(current); // dir & lang settings for current node, explicit or inherited list.push(current); }else{ // Meta data about this non-widget node current = { node: node, scripts: scripts, parent: parent }; } // Recurse, collecting <script type="dojo/..."> children, and also looking for // descendant nodes with dojoType specified (unless the widget has the stopParser flag). // When finished with children, go to my next sibling. scripts = childScripts; scriptsOnly = node.stopParser || (ctor && ctor.prototype.stopParser && !(options.template)); parent = current; node = firstChild; } var d = new Deferred(); // If there are modules to load then require them in if(mids.length){ // Warn that there are modules being auto-required if(has("dojo-debug-messages")){ console.warn("WARNING: Modules being Auto-Required: " + mids.join(", ")); } var r = options.contextRequire || require; r(mids, function(){ // Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't // be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to // auto-require of a module like ContentPane. Assumes list is in DFS order. d.resolve(darray.filter(list, function(widget){ if(!widget.ctor){ // Attempt to find the constructor again. Still won't find classes defined via // dijit/Declaration so need to try/catch. try{ widget.ctor = getCtor(widget.types, options.contextRequire); }catch(e){} } // Get the parent widget var parent = widget.parent; while(parent && !parent.types){ parent = parent.parent; } // Return false if this node should be skipped due to stopParser on an ancestor. // Since list[] is in DFS order, this loop will always set parent.instantiateChildren before // trying to compute widget.instantiate. var proto = widget.ctor && widget.ctor.prototype; widget.instantiateChildren = !(proto && proto.stopParser && !(options.template)); widget.instantiate = !parent || (parent.instantiate && parent.instantiateChildren); return widget.instantiate; })); }); }else{ // There were no modules to load, so just resolve with the parsed nodes. This separate code path is for // efficiency, to avoid running the require() and the callback code above. d.resolve(list); } // Return the promise return d.promise; }, _require: function(/*DOMNode*/ script, /*Object?*/ options){ // summary: // Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node, // calls require() to load the specified modules and (asynchronously) assign them to the specified global // variables, and returns a Promise for when that operation completes. // // In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }). var hash = myEval("{" + script.innerHTML + "}"), // can't use dojo/json::parse() because maybe no quotes vars = [], mids = [], d = new Deferred(); var contextRequire = (options && options.contextRequire) || require; for(var name in hash){ vars.push(name); mids.push(hash[name]); } contextRequire(mids, function(){ for(var i = 0; i < vars.length; i++){ dlang.setObject(vars[i], arguments[i]); } d.resolve(arguments); }); return d.promise; }, _scanAmd: function(root, options){ // summary: // Scans the DOM for any declarative requires and returns their values. // description: // Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the // specified modules and (asynchronously) assign them to the specified global variables, // and returns a Promise for when those operations complete. // root: DomNode // The node to base the scan from. // options: Object? // a kwArgs options object, see parse() for details // Promise that resolves when all the <script type=dojo/require> nodes have finished loading. var deferred = new Deferred(), promise = deferred.promise; deferred.resolve(true); var self = this; query("script[type='dojo/require']", root).forEach(function(node){ // Fire off require() call for specified modules. Chain this require to fire after // any previous requires complete, so that layers can be loaded before individual module require()'s fire. promise = promise.then(function(){ return self._require(node, options); }); // Remove from DOM so it isn't seen again node.parentNode.removeChild(node); }); return promise; }, parse: function(rootNode, options){ // summary: // Scan the DOM for class instances, and instantiate them. // description: // Search specified node (or root node) recursively for class instances, // and instantiate them. Searches for either data-dojo-type="Class" or // dojoType="Class" where "Class" is a a fully qualified class name, // like `dijit/form/Button` // // Using `data-dojo-type`: // Attributes using can be mixed into the parameters used to instantiate the // Class by using a `data-dojo-props` attribute on the node being converted. // `data-dojo-props` should be a string attribute to be converted from JSON. // // Using `dojoType`: // Attributes are read from the original domNode and converted to appropriate // types by looking up the Class prototype values. This is the default behavior // from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will // go away in Dojo 2.0. // rootNode: DomNode? // A default starting root node from which to start the parsing. Can be // omitted, defaulting to the entire document. If omitted, the `options` // object can be passed in this place. If the `options` object has a // `rootNode` member, that is used. // options: Object? // A hash of options. // // - noStart: Boolean?: // when set will prevent the parser from calling .startup() // when locating the nodes. // - rootNode: DomNode?: // identical to the function's `rootNode` argument, though // allowed to be passed in via this `options object. // - template: Boolean: // If true, ignores ContentPane's stopParser flag and parses contents inside of // a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes // nested inside the ContentPane to work. // - inherited: Object: // Hash possibly containing dir and lang settings to be applied to // parsed widgets, unless there's another setting on a sub-node that overrides // - scope: String: // Root for attribute names to search for. If scopeName is dojo, // will search for data-dojo-type (or dojoType). For backwards compatibility // reasons defaults to dojo._scopeName (which is "dojo" except when // multi-version support is used, when it will be something like dojo16, dojo20, etc.) // - propsThis: Object: // If specified, "this" referenced from data-dojo-props will refer to propsThis. // Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin` // - contextRequire: Function: // If specified, this require is utilised for looking resolving modules instead of the // `dojo/parser` context `require()`. Intended for use from the widgets-in-template feature of // `dijit._WidgetsInTemplateMixin`. // returns: Mixed // Returns a blended object that is an array of the instantiated objects, but also can include // a promise that is resolved with the instantiated objects. This is done for backwards // compatibility. If the parser auto-requires modules, it will always behave in a promise // fashion and `parser.parse().then(function(instances){...})` should be used. // example: // Parse all widgets on a page: // | parser.parse(); // example: // Parse all classes within the node with id="foo" // | parser.parse(dojo.byId('foo')); // example: // Parse all classes in a page, but do not call .startup() on any // child // | parser.parse({ noStart: true }) // example: // Parse all classes in a node, but do not call .startup() // | parser.parse(someNode, { noStart:true }); // | // or // | parser.parse({ noStart:true, rootNode: someNode }); // determine the root node and options based on the passed arguments. var root; if(!options && rootNode && rootNode.rootNode){ options = rootNode; root = options.rootNode; }else if(rootNode && dlang.isObject(rootNode) && !("nodeType" in rootNode)){ options = rootNode; }else{ root = rootNode; } root = root ? dom.byId(root) : dwindow.body(); options = options || {}; var mixin = options.template ? { template: true } : {}, instances = [], self = this; // First scan for any <script type=dojo/require> nodes, and execute. // Then scan for all nodes with data-dojo-type, and load any unloaded modules. // Then build the object instances. Add instances to already existing (but empty) instances[] array, // which may already have been returned to caller. Also, use otherwise to collect and throw any errors // that occur during the parse(). var p = this._scanAmd(root, options).then(function(){ return self.scan(root, options); }).then(function(parsedNodes){ return self._instantiate(parsedNodes, mixin, options, true); }).then(function(_instances){ // Copy the instances into the instances[] array we declared above, and are accessing as // our return value. return instances = instances.concat(_instances); }).otherwise(function(e){ // TODO Modify to follow better pattern for promise error management when available console.error("dojo/parser::parse() error", e); throw e; }); // Blend the array with the promise dlang.mixin(instances, p); return instances; } }; if(has("extend-dojo")){ dojo.parser = parser; } // Register the parser callback. It should be the first callback // after the a11y test. if(config.parseOnLoad){ ready(100, parser, "parse"); } return parser; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 | 1 1 | define(["./_base/kernel", "./has", "./dom", "./on", "./_base/array", "./_base/lang", "./selector/_loader", "./selector/_loader!default"], function(dojo, has, dom, on, array, lang, loader, defaultEngine){ "use strict"; has.add("array-extensible", function(){ // test to see if we can extend an array (not supported in old IE) return lang.delegate([], {length: 1}).length == 1 && !has("bug-for-in-skips-shadowed"); }); var ap = Array.prototype, aps = ap.slice, apc = ap.concat, forEach = array.forEach; var tnl = function(/*Array*/ a, /*dojo/NodeList?*/ parent, /*Function?*/ NodeListCtor){ // summary: // decorate an array to make it look like a `dojo/NodeList`. // a: // Array of nodes to decorate. // parent: // An optional parent NodeList that generated the current // list of nodes. Used to call _stash() so the parent NodeList // can be accessed via end() later. // NodeListCtor: // An optional constructor function to use for any // new NodeList calls. This allows a certain chain of // NodeList calls to use a different object than dojo/NodeList. var nodeList = new (NodeListCtor || this._NodeListCtor || nl)(a); return parent ? nodeList._stash(parent) : nodeList; }; var loopBody = function(f, a, o){ a = [0].concat(aps.call(a, 0)); o = o || dojo.global; return function(node){ a[0] = node; return f.apply(o, a); }; }; // adapters var adaptAsForEach = function(f, o){ // summary: // adapts a single node function to be used in the forEach-type // actions. The initial object is returned from the specialized // function. // f: Function // a function to adapt // o: Object? // an optional context for f return function(){ this.forEach(loopBody(f, arguments, o)); return this; // Object }; }; var adaptAsMap = function(f, o){ // summary: // adapts a single node function to be used in the map-type // actions. The return is a new array of values, as via `dojo/_base/array.map` // f: Function // a function to adapt // o: Object? // an optional context for f return function(){ return this.map(loopBody(f, arguments, o)); }; }; var adaptAsFilter = function(f, o){ // summary: // adapts a single node function to be used in the filter-type actions // f: Function // a function to adapt // o: Object? // an optional context for f return function(){ return this.filter(loopBody(f, arguments, o)); }; }; var adaptWithCondition = function(f, g, o){ // summary: // adapts a single node function to be used in the map-type // actions, behaves like forEach() or map() depending on arguments // f: Function // a function to adapt // g: Function // a condition function, if true runs as map(), otherwise runs as forEach() // o: Object? // an optional context for f and g return function(){ var a = arguments, body = loopBody(f, a, o); if(g.call(o || dojo.global, a)){ return this.map(body); // self } this.forEach(body); return this; // self }; }; var NodeList = function(array){ // summary: // Array-like object which adds syntactic // sugar for chaining, common iteration operations, animation, and // node manipulation. NodeLists are most often returned as the // result of dojo/query() calls. // description: // NodeList instances provide many utilities that reflect // core Dojo APIs for Array iteration and manipulation, DOM // manipulation, and event handling. Instead of needing to dig up // functions in the dojo package, NodeLists generally make the // full power of Dojo available for DOM manipulation tasks in a // simple, chainable way. // example: // create a node list from a node // | require(["dojo/query", "dojo/dom" // | ], function(query, dom){ // | query.NodeList(dom.byId("foo")); // | }); // example: // get a NodeList from a CSS query and iterate on it // | require(["dojo/on", "dojo/dom" // | ], function(on, dom){ // | var l = query(".thinger"); // | l.forEach(function(node, index, nodeList){ // | console.log(index, node.innerHTML); // | }); // | }); // example: // use native and Dojo-provided array methods to manipulate a // NodeList without needing to use dojo.* functions explicitly: // | require(["dojo/query", "dojo/dom-construct", "dojo/dom" // | ], function(query, domConstruct, dom){ // | var l = query(".thinger"); // | // since NodeLists are real arrays, they have a length // | // property that is both readable and writable and // | // push/pop/shift/unshift methods // | console.log(l.length); // | l.push(domConstruct.create("span")); // | // | // dojo's normalized array methods work too: // | console.log( l.indexOf(dom.byId("foo")) ); // | // ...including the special "function as string" shorthand // | console.log( l.every("item.nodeType == 1") ); // | // | // NodeLists can be [..] indexed, or you can use the at() // | // function to get specific items wrapped in a new NodeList: // | var node = l[3]; // the 4th element // | var newList = l.at(1, 3); // the 2nd and 4th elements // | }); // example: // chainability is a key advantage of NodeLists: // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query(".thinger") // | .onclick(function(e){ /* ... */ }) // | .at(1, 3, 8) // get a subset // | .style("padding", "5px") // | .forEach(console.log); // | }); var isNew = this instanceof nl && has("array-extensible"); if(typeof array == "number"){ array = Array(array); } var nodeArray = (array && "length" in array) ? array : arguments; if(isNew || !nodeArray.sort){ // make sure it's a real array before we pass it on to be wrapped var target = isNew ? this : [], l = target.length = nodeArray.length; for(var i = 0; i < l; i++){ target[i] = nodeArray[i]; } if(isNew){ // called with new operator, this means we are going to use this instance and push // the nodes on to it. This is usually much faster since the NodeList properties // don't need to be copied (unless the list of nodes is extremely large). return target; } nodeArray = target; } // called without new operator, use a real array and copy prototype properties, // this is slower and exists for back-compat. Should be removed in 2.0. lang._mixin(nodeArray, nlp); nodeArray._NodeListCtor = function(array){ // call without new operator to preserve back-compat behavior return nl(array); }; return nodeArray; }; var nl = NodeList, nlp = nl.prototype = has("array-extensible") ? [] : {};// extend an array if it is extensible // expose adapters and the wrapper as private functions nl._wrap = nlp._wrap = tnl; nl._adaptAsMap = adaptAsMap; nl._adaptAsForEach = adaptAsForEach; nl._adaptAsFilter = adaptAsFilter; nl._adaptWithCondition = adaptWithCondition; // mass assignment // add array redirectors forEach(["slice", "splice"], function(name){ var f = ap[name]; //Use a copy of the this array via this.slice() to allow .end() to work right in the splice case. // CANNOT apply ._stash()/end() to splice since it currently modifies // the existing this array -- it would break backward compatibility if we copy the array before // the splice so that we can use .end(). So only doing the stash option to this._wrap for slice. nlp[name] = function(){ return this._wrap(f.apply(this, arguments), name == "slice" ? this : null); }; }); // concat should be here but some browsers with native NodeList have problems with it // add array.js redirectors forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){ var f = array[name]; nlp[name] = function(){ return f.apply(dojo, [this].concat(aps.call(arguments, 0))); }; }); lang.extend(NodeList, { // copy the constructors constructor: nl, _NodeListCtor: nl, toString: function(){ // Array.prototype.toString can't be applied to objects, so we use join return this.join(","); }, _stash: function(parent){ // summary: // private function to hold to a parent NodeList. end() to return the parent NodeList. // // example: // How to make a `dojo/NodeList` method that only returns the third node in // the dojo/NodeList but allows access to the original NodeList by using this._stash: // | require(["dojo/query", "dojo/_base/lang", "dojo/NodeList", "dojo/NodeList-dom" // | ], function(query, lang){ // | lang.extend(NodeList, { // | third: function(){ // | var newNodeList = NodeList(this[2]); // | return newNodeList._stash(this); // | } // | }); // | // then see how _stash applies a sub-list, to be .end()'ed out of // | query(".foo") // | .third() // | .addClass("thirdFoo") // | .end() // | // access to the orig .foo list // | .removeClass("foo") // | }); // this._parent = parent; return this; // dojo/NodeList }, on: function(eventName, listener){ // summary: // Listen for events on the nodes in the NodeList. Basic usage is: // // example: // | require(["dojo/query" // | ], function(query){ // | query(".my-class").on("click", listener); // This supports event delegation by using selectors as the first argument with the event names as // pseudo selectors. For example: // | query("#my-list").on("li:click", listener); // This will listen for click events within `<li>` elements that are inside the `#my-list` element. // Because on supports CSS selector syntax, we can use comma-delimited events as well: // | query("#my-list").on("li button:mouseover, li:click", listener); // | }); var handles = this.map(function(node){ return on(node, eventName, listener); // TODO: apply to the NodeList so the same selector engine is used for matches }); handles.remove = function(){ for(var i = 0; i < handles.length; i++){ handles[i].remove(); } }; return handles; }, end: function(){ // summary: // Ends use of the current `NodeList` by returning the previous NodeList // that generated the current NodeList. // description: // Returns the `NodeList` that generated the current `NodeList`. If there // is no parent NodeList, an empty NodeList is returned. // example: // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("a") // | .filter(".disabled") // | // operate on the anchors that only have a disabled class // | .style("color", "grey") // | .end() // | // jump back to the list of anchors // | .style(...) // | }); // if(this._parent){ return this._parent; }else{ //Just return empty list. return new this._NodeListCtor(0); } }, // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods // FIXME: handle return values for #3244 // http://trac.dojotoolkit.org/ticket/3244 // FIXME: // need to wrap or implement: // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?) // reduce // reduceRight /*===== slice: function(begin, end){ // summary: // Returns a new NodeList, maintaining this one in place // description: // This method behaves exactly like the Array.slice method // with the caveat that it returns a `dojo/NodeList` and not a // raw Array. For more details, see Mozilla's [slice // documentation](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/slice) // begin: Integer // Can be a positive or negative integer, with positive // integers noting the offset to begin at, and negative // integers denoting an offset from the end (i.e., to the left // of the end) // end: Integer? // Optional parameter to describe what position relative to // the NodeList's zero index to end the slice at. Like begin, // can be positive or negative. return this._wrap(a.slice.apply(this, arguments)); }, splice: function(index, howmany, item){ // summary: // Returns a new NodeList, manipulating this NodeList based on // the arguments passed, potentially splicing in new elements // at an offset, optionally deleting elements // description: // This method behaves exactly like the Array.splice method // with the caveat that it returns a `dojo/NodeList` and not a // raw Array. For more details, see Mozilla's [splice // documentation](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) // For backwards compatibility, calling .end() on the spliced NodeList // does not return the original NodeList -- splice alters the NodeList in place. // index: Integer // begin can be a positive or negative integer, with positive // integers noting the offset to begin at, and negative // integers denoting an offset from the end (i.e., to the left // of the end) // howmany: Integer? // Optional parameter to describe what position relative to // the NodeList's zero index to end the slice at. Like begin, // can be positive or negative. // item: Object...? // Any number of optional parameters may be passed in to be // spliced into the NodeList return this._wrap(a.splice.apply(this, arguments)); // dojo/NodeList }, indexOf: function(value, fromIndex){ // summary: // see `dojo/_base/array.indexOf()`. The primary difference is that the acted-on // array is implicitly this NodeList // value: Object // The value to search for. // fromIndex: Integer? // The location to start searching from. Optional. Defaults to 0. // description: // For more details on the behavior of indexOf, see Mozilla's // [indexOf // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf) // returns: // Positive Integer or 0 for a match, -1 of not found. return d.indexOf(this, value, fromIndex); // Integer }, lastIndexOf: function(value, fromIndex){ // summary: // see `dojo/_base/array.lastIndexOf()`. The primary difference is that the // acted-on array is implicitly this NodeList // description: // For more details on the behavior of lastIndexOf, see // Mozilla's [lastIndexOf // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf) // value: Object // The value to search for. // fromIndex: Integer? // The location to start searching from. Optional. Defaults to 0. // returns: // Positive Integer or 0 for a match, -1 of not found. return d.lastIndexOf(this, value, fromIndex); // Integer }, every: function(callback, thisObject){ // summary: // see `dojo/_base/array.every()` and the [Array.every // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every). // Takes the same structure of arguments and returns as // dojo/_base/array.every() with the caveat that the passed array is // implicitly this NodeList // callback: Function // the callback // thisObject: Object? // the context return d.every(this, callback, thisObject); // Boolean }, some: function(callback, thisObject){ // summary: // Takes the same structure of arguments and returns as // `dojo/_base/array.some()` with the caveat that the passed array is // implicitly this NodeList. See `dojo/_base/array.some()` and Mozilla's // [Array.some // documentation](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some). // callback: Function // the callback // thisObject: Object? // the context return d.some(this, callback, thisObject); // Boolean }, =====*/ concat: function(item){ // summary: // Returns a new NodeList comprised of items in this NodeList // as well as items passed in as parameters // description: // This method behaves exactly like the Array.concat method // with the caveat that it returns a `NodeList` and not a // raw Array. For more details, see the [Array.concat // docs](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/concat) // item: Object? // Any number of optional parameters may be passed in to be // spliced into the NodeList //return this._wrap(apc.apply(this, arguments)); // the line above won't work for the native NodeList, or for Dojo NodeLists either :-( // implementation notes: // Array.concat() doesn't recognize native NodeLists or Dojo NodeLists // as arrays, and so does not inline them into a unioned array, but // appends them as single entities. Both the original NodeList and the // items passed in as parameters must be converted to raw Arrays // and then the concatenation result may be re-_wrap()ed as a Dojo NodeList. var t = aps.call(this, 0), m = array.map(arguments, function(a){ return aps.call(a, 0); }); return this._wrap(apc.apply(t, m), this); // dojo/NodeList }, map: function(/*Function*/ func, /*Function?*/ obj){ // summary: // see `dojo/_base/array.map()`. The primary difference is that the acted-on // array is implicitly this NodeList and the return is a // NodeList (a subclass of Array) return this._wrap(array.map(this, func, obj), this); // dojo/NodeList }, forEach: function(callback, thisObj){ // summary: // see `dojo/_base/array.forEach()`. The primary difference is that the acted-on // array is implicitly this NodeList. If you want the option to break out // of the forEach loop, use every() or some() instead. forEach(this, callback, thisObj); // non-standard return to allow easier chaining return this; // dojo/NodeList }, filter: function(/*String|Function*/ filter){ // summary: // "masks" the built-in javascript filter() method (supported // in Dojo via `dojo/_base/array.filter`) to support passing a simple // string filter in addition to supporting filtering function // objects. // filter: // If a string, a CSS rule like ".thinger" or "div > span". // example: // "regular" JS filter syntax as exposed in `dojo/_base/array.filter`: // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("*").filter(function(item){ // | // highlight every paragraph // | return (item.nodeName == "p"); // | }).style("backgroundColor", "yellow"); // | }); // example: // the same filtering using a CSS selector // | require(["dojo/query", "dojo/NodeList-dom" // | ], function(query){ // | query("*").filter("p").styles("backgroundColor", "yellow"); // | }); var a = arguments, items = this, start = 0; if(typeof filter == "string"){ // inline'd type check items = query._filterResult(this, a[0]); if(a.length == 1){ // if we only got a string query, pass back the filtered results return items._stash(this); // dojo/NodeList } // if we got a callback, run it over the filtered items start = 1; } return this._wrap(array.filter(items, a[start], a[start + 1]), this); // dojo/NodeList }, instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){ // summary: // Create a new instance of a specified class, using the // specified properties and each node in the NodeList as a // srcNodeRef. // example: // Grabs all buttons in the page and converts them to dijit/form/Button's. // | var buttons = query("button").instantiate(Button, {showLabel: true}); var c = lang.isFunction(declaredClass) ? declaredClass : lang.getObject(declaredClass); properties = properties || {}; return this.forEach(function(node){ new c(properties, node); }); // dojo/NodeList }, at: function(/*===== index =====*/){ // summary: // Returns a new NodeList comprised of items in this NodeList // at the given index or indices. // // index: Integer... // One or more 0-based indices of items in the current // NodeList. A negative index will start at the end of the // list and go backwards. // // example: // Shorten the list to the first, second, and third elements // | require(["dojo/query" // | ], function(query){ // | query("a").at(0, 1, 2).forEach(fn); // | }); // // example: // Retrieve the first and last elements of a unordered list: // | require(["dojo/query" // | ], function(query){ // | query("ul > li").at(0, -1).forEach(cb); // | }); // // example: // Do something for the first element only, but end() out back to // the original list and continue chaining: // | require(["dojo/query" // | ], function(query){ // | query("a").at(0).onclick(fn).end().forEach(function(n){ // | console.log(n); // all anchors on the page. // | }) // | }); var t = new this._NodeListCtor(0); forEach(arguments, function(i){ if(i < 0){ i = this.length + i; } if(this[i]){ t.push(this[i]); } }, this); return t._stash(this); // dojo/NodeList } }); function queryForEngine(engine, NodeList){ var query = function(/*String*/ query, /*String|DOMNode?*/ root){ // summary: // Returns nodes which match the given CSS selector, searching the // entire document by default but optionally taking a node to scope // the search by. Returns an instance of NodeList. if(typeof root == "string"){ root = dom.byId(root); if(!root){ return new NodeList([]); } } var results = typeof query == "string" ? engine(query, root) : query ? (query.end && query.on) ? query : [query] : []; if(results.end && results.on){ // already wrapped return results; } return new NodeList(results); }; query.matches = engine.match || function(node, selector, root){ // summary: // Test to see if a node matches a selector return query.filter([node], selector, root).length > 0; }; // the engine provides a filtering function, use it to for matching query.filter = engine.filter || function(nodes, selector, root){ // summary: // Filters an array of nodes. Note that this does not guarantee to return a NodeList, just an array. return query(selector, root).filter(function(node){ return array.indexOf(nodes, node) > -1; }); }; if(typeof engine != "function"){ var search = engine.search; engine = function(selector, root){ // Slick does it backwards (or everyone else does it backwards, probably the latter) return search(root || document, selector); }; } return query; } var query = queryForEngine(defaultEngine, NodeList); /*===== query = function(selector, context){ // summary: // This modules provides DOM querying functionality. The module export is a function // that can be used to query for DOM nodes by CSS selector and returns a NodeList // representing the matching nodes. // selector: String // A CSS selector to search for. // context: String|DomNode? // An optional context to limit the searching scope. Only nodes under `context` will be // scanned. // example: // add an onclick handler to every submit button in the document // which causes the form to be sent via Ajax instead: // | require(["dojo/query", "dojo/request", "dojo/dom-form", "dojo/dom-construct", "dojo/dom-style" // | ], function(query, request, domForm, domConstruct, domStyle){ // | query("input[type='submit']").on("click", function(e){ // | e.preventDefault(); // prevent sending the form // | var btn = e.target; // | request.post("http://example.com/", { // | data: domForm.toObject(btn.form) // | }).then(function(response){ // | // replace the form with the response // | domConstruct.create(div, {innerHTML: response}, btn.form, "after"); // | domStyle.set(btn.form, "display", "none"); // | }); // | }); // | }); // // description: // dojo/query is responsible for loading the appropriate query engine and wrapping // its results with a `NodeList`. You can use dojo/query with a specific selector engine // by using it as a plugin. For example, if you installed the sizzle package, you could // use it as the selector engine with: // | require(["dojo/query!sizzle"], function(query){ // | query("div")... // // The id after the ! can be a module id of the selector engine or one of the following values: // // - acme: This is the default engine used by Dojo base, and will ensure that the full // Acme engine is always loaded. // // - css2: If the browser has a native selector engine, this will be used, otherwise a // very minimal lightweight selector engine will be loaded that can do simple CSS2 selectors // (by #id, .class, tag, and [name=value] attributes, with standard child or descendant (>) // operators) and nothing more. // // - css2.1: If the browser has a native selector engine, this will be used, otherwise the // full Acme engine will be loaded. // // - css3: If the browser has a native selector engine with support for CSS3 pseudo // selectors (most modern browsers except IE8), this will be used, otherwise the // full Acme engine will be loaded. // // - Or the module id of a selector engine can be used to explicitly choose the selector engine // // For example, if you are using CSS3 pseudo selectors in module, you can specify that // you will need support them with: // | require(["dojo/query!css3"], function(query){ // | query('#t > h3:nth-child(odd)')... // // You can also choose the selector engine/load configuration by setting the query-selector: // For example: // | <script data-dojo-config="query-selector:'css3'" src="dojo.js"></script> // return new NodeList(); // dojo/NodeList }; =====*/ // the query that is returned from this module is slightly different than dojo.query, // because dojo.query has to maintain backwards compatibility with returning a // true array which has performance problems. The query returned from the module // does not use true arrays, but rather inherits from Array, making it much faster to // instantiate. dojo.query = queryForEngine(defaultEngine, function(array){ // call it without the new operator to invoke the back-compat behavior that returns a true array return NodeList(array); // dojo/NodeList }); query.load = function(id, parentRequire, loaded){ // summary: // can be used as AMD plugin to conditionally load new query engine // example: // | require(["dojo/query!custom"], function(qsa){ // | // loaded selector/custom.js as engine // | qsa("#foobar").forEach(...); // | }); loader.load(id, parentRequire, function(engine){ loaded(queryForEngine(engine, NodeList)); }); }; dojo._filterQueryResult = query._filterResult = function(nodes, selector, root){ return new NodeList(query.filter(nodes, selector, root)); }; dojo.NodeList = query.NodeList = NodeList; return query; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 1 | define(["./_base/kernel", "./has", "require", "./has!host-browser?./domReady", "./_base/lang"], function(dojo, has, require, domReady, lang){ // module: // dojo/ready // note: // This module should be unnecessary in dojo 2.0 var // truthy if DOMContentLoaded or better (e.g., window.onload fired) has been achieved isDomReady = 0, // The queue of functions waiting to execute as soon as dojo.ready conditions satisfied loadQ = [], // prevent recursion in onLoad onLoadRecursiveGuard = 0, handleDomReady = function(){ isDomReady = 1; dojo._postLoad = dojo.config.afterOnLoad = true; onEvent(); }, onEvent = function(){ // Called when some state changes: // - dom ready // - dojo/domReady has finished processing everything in its queue // - task added to loadQ // - require() has finished loading all currently requested modules // // Run the functions queued with dojo.ready if appropriate. //guard against recursions into this function if(onLoadRecursiveGuard){ return; } onLoadRecursiveGuard = 1; // Run tasks in queue if require() is finished loading modules, the dom is ready, and there are no // pending tasks registered via domReady(). // The last step is necessary so that a user defined dojo.ready() callback is delayed until after the // domReady() calls inside of dojo. Failure can be seen on dijit/tests/robot/Dialog_ally.html on IE8 // because the dijit/focus.js domReady() callback doesn't execute until after the test starts running. while(isDomReady && (!domReady || domReady._Q.length == 0) && (require.idle ? require.idle() : true) && loadQ.length){ var f = loadQ.shift(); try{ f(); }catch(e){ // force the dojo.js on("error") handler do display the message e.info = e.message; if(require.signal){ require.signal("error", e); }else{ throw e; } } } onLoadRecursiveGuard = 0; }; // Check if we should run the next queue operation whenever require() finishes loading modules or domReady // finishes processing it's queue. require.on && require.on("idle", onEvent); if(domReady){ domReady._onQEmpty = onEvent; } var ready = dojo.ready = dojo.addOnLoad = function(priority, context, callback){ // summary: // Add a function to execute on DOM content loaded and all requested modules have arrived and been evaluated. // In most cases, the `domReady` plug-in should suffice and this method should not be needed. // // When called in a non-browser environment, just checks that all requested modules have arrived and been // evaluated. // priority: Integer? // The order in which to exec this callback relative to other callbacks, defaults to 1000 // context: Object?|Function // The context in which to run execute callback, or a callback if not using context // callback: Function? // The function to execute. // // example: // Simple DOM and Modules ready syntax // | require(["dojo/ready"], function(ready){ // | ready(function(){ alert("Dom ready!"); }); // | }); // // example: // Using a priority // | require(["dojo/ready"], function(ready){ // | ready(2, function(){ alert("low priority ready!"); }) // | }); // // example: // Using context // | require(["dojo/ready"], function(ready){ // | ready(foo, function(){ // | // in here, this == foo // | }); // | }); // // example: // Using dojo/hitch style args: // | require(["dojo/ready"], function(ready){ // | var foo = { dojoReady: function(){ console.warn(this, "dojo dom and modules ready."); } }; // | ready(foo, "dojoReady"); // | }); var hitchArgs = lang._toArray(arguments); if(typeof priority != "number"){ callback = context; context = priority; priority = 1000; }else{ hitchArgs.shift(); } callback = callback ? lang.hitch.apply(dojo, hitchArgs) : function(){ context(); }; callback.priority = priority; for(var i = 0; i < loadQ.length && priority >= loadQ[i].priority; i++){} loadQ.splice(i, 0, callback); onEvent(); }; has.add("dojo-config-addOnLoad", 1); if(has("dojo-config-addOnLoad")){ var dca = dojo.config.addOnLoad; if(dca){ ready[(lang.isArray(dca) ? "apply" : "call")](dojo, dca); } } if(has("dojo-sync-loader") && dojo.config.parseOnLoad && !dojo.isAsync){ ready(99, function(){ if(!dojo.parser){ dojo.deprecated("Add explicit require(['dojo/parser']);", "", "2.0"); require(["dojo/parser"]); } }); } if(domReady){ domReady(handleDomReady); }else{ handleDomReady(); } return ready; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 1 | define(["./_base/kernel", "./_base/lang"], function(dojo, lang){ // module: // dojo/regexp var regexp = { // summary: // Regular expressions and Builder resources }; lang.setObject("dojo.regexp", regexp); regexp.escapeString = function(/*String*/str, /*String?*/except){ // summary: // Adds escape sequences for special characters in regular expressions // except: // a String with special characters to be left unescaped return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){ if(except && except.indexOf(ch) != -1){ return ch; } return "\\" + ch; }); // String }; regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){ // summary: // Builds a regular expression that groups subexpressions // description: // A utility function used by some of the RE generators. The // subexpressions are constructed by the function, re, in the second // parameter. re builds one subexpression for each elem in the array // a, in the first parameter. Returns a string for a regular // expression that groups all the subexpressions. // arr: // A single value or an array of values. // re: // A function. Takes one parameter and converts it to a regular // expression. // nonCapture: // If true, uses non-capturing match, otherwise matches are retained // by regular expression. Defaults to false // case 1: a is a single value. if(!(arr instanceof Array)){ return re(arr); // String } // case 2: a is an array var b = []; for(var i = 0; i < arr.length; i++){ // convert each elem to a RE b.push(re(arr[i])); } // join the REs as alternatives in a RE group. return regexp.group(b.join("|"), nonCapture); // String }; regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){ // summary: // adds group match to expression // nonCapture: // If true, uses non-capturing match, otherwise matches are retained // by regular expression. return "(" + (nonCapture ? "?:":"") + expression + ")"; // String }; return regexp; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 1 | define([ './request/default!'/*=====, './_base/declare', './promise/Promise' =====*/ ], function(request/*=====, declare, Promise =====*/){ /*===== request = function(url, options){ // summary: // Send a request using the default transport for the current platform. // url: String // The URL to request. // options: dojo/request.__Options? // Options for the request. // returns: dojo/request.__Promise }; request.__Promise = declare(Promise, { // response: dojo/promise/Promise // A promise resolving to an object representing // the response from the server. }); request.__BaseOptions = declare(null, { // query: String|Object? // Query parameters to append to the URL. // data: String|Object? // Data to transfer. This is ignored for GET and DELETE // requests. // preventCache: Boolean? // Whether to append a cache-busting parameter to the URL. // timeout: Integer? // Milliseconds to wait for the response. If this time // passes, the then the promise is rejected. // handleAs: String? // How to handle the response from the server. Default is // 'text'. Other values are 'json', 'javascript', and 'xml'. }); request.__MethodOptions = declare(null, { // method: String? // The HTTP method to use to make the request. Must be // uppercase. }); request.__Options = declare([request.__BaseOptions, request.__MethodOptions]); request.get = function(url, options){ // summary: // Send an HTTP GET request using the default transport for the current platform. // url: String // URL to request // options: dojo/request.__BaseOptions? // Options for the request. // returns: dojo/request.__Promise }; request.post = function(url, options){ // summary: // Send an HTTP POST request using the default transport for the current platform. // url: String // URL to request // options: dojo/request.__BaseOptions? // Options for the request. // returns: dojo/request.__Promise }; request.put = function(url, options){ // summary: // Send an HTTP POST request using the default transport for the current platform. // url: String // URL to request // options: dojo/request.__BaseOptions? // Options for the request. // returns: dojo/request.__Promise }; request.del = function(url, options){ // summary: // Send an HTTP DELETE request using the default transport for the current platform. // url: String // URL to request // options: dojo/request.__BaseOptions? // Options for the request. // returns: dojo/request.__Promise }; =====*/ return request; }); |
| 1 2 3 4 5 6 7 8 9 | 1 | define(["./_base/loader"], function(loader){ return { dynamic:0, normalize:function(id){return id;}, load:loader.require }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | 1 | define([ "./_base/array", "./dom", "./dom-geometry", "./_base/kernel", "./_base/lang", "./_base/window", "doh/_browserRunner", "doh/robot", "./window" ], function(array, dom, geom, kernel, lang, win, doh, robot, winUtils){ kernel.experimental("dojo.robot"); // users who use doh+dojo get the added convenience of robot.mouseMoveAt(), // instead of computing the absolute coordinates of their elements themselves lang.mixin(robot, { _resolveNode: function(/*String||DOMNode||Function*/ n){ if(typeof n == "function"){ // if the user passed a function returning a node, evaluate it n = n(); } return n ? dom.byId(n) : null; }, _scrollIntoView: function(/*Node*/ n){ // scrolls the passed node into view, scrolling all ancestor frames/windows as well. // Assumes parent iframes can be made fully visible given the current browser window size var p = null; array.forEach(robot._getWindowChain(n), function(w){ // get the position of the node wrt its parent window // if it is a parent frame, its padding and border extents will get added in var p2 = geom.position(n, false), b = geom.getPadBorderExtents(n), oldp = null; // if p2 is the position of the original passed node, store the position away as p // otherwise, node is actually an iframe. in this case, add the iframe's position wrt its parent window and also the iframe's padding and border extents if(!p){ p = p2; }else{ oldp = p; p = {x: p.x+p2.x+b.l, y: p.y+p2.y+b.t, w: p.w, h: p.h}; } // scroll the parent window so that the node translated into the parent window's coordinate space is in view winUtils.scrollIntoView(n,p); // adjust position for the new scroll offsets p2 = geom.position(n, false); if(!oldp){ p = p2; }else{ p = {x: oldp.x+p2.x+b.l, y: oldp.y+p2.y+b.t, w: p.w, h: p.h}; } // get the parent iframe so it can be scrolled too n = w.frameElement; }); }, _position: function(/*Node*/ n){ // Returns the geom.position of the passed node wrt the passed window's viewport, // following any parent iframes containing the node and clipping the node to each iframe. // precondition: _scrollIntoView already called var p = null, max = Math.max, min = Math.min; // p: the returned position of the node array.forEach(robot._getWindowChain(n), function(w){ // get the position of the node wrt its parent window // if it is a parent frame, its padding and border extents will get added in var p2 = geom.position(n, false), b = geom.getPadBorderExtents(n); // if p2 is the position of the original passed node, store the position away as p // otherwise, node is actually an iframe. in this case, add the iframe's position wrt its parent window and also the iframe's padding and border extents if(!p){ p = p2; }else{ var view = winUtils.getBox(n.contentWindow.document); p2.r = p2.x+view.w; p2.b = p2.y+view.h; p = {x: max(p.x+p2.x,p2.x)+b.l, // clip left edge of node wrt the iframe y: max(p.y+p2.y,p2.y)+b.t, // top edge r: min(p.x+p2.x+p.w,p2.r)+b.l, // right edge (to compute width) b: min(p.y+p2.y+p.h,p2.b)+b.t}; // bottom edge (to compute height) // save a few bytes by computing width and height from r and b p.w = p.r-p.x; p.h = p.b-p.y; } // the new node is now the old node's parent iframe n=w.frameElement; }); return p; }, _getWindowChain : function(/*Node*/ n){ // Returns an array of windows starting from the passed node's parent window and ending at dojo's window var cW = winUtils.get(n.ownerDocument); var arr = [cW]; var f = cW.frameElement; return (cW == win.global || !f) ? arr : arr.concat(robot._getWindowChain(f)); }, scrollIntoView : function(/*String||DOMNode||Function*/ node, /*Number, optional*/ delay){ // summary: // Scroll the passed node into view, if it is not. // node: // The id of the node, or the node itself, to move the mouse to. // If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes. // This is useful if you need to move the mouse to an node that is not yet present. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. robot.sequence(function(){ robot._scrollIntoView(robot._resolveNode(node)); }, delay); }, mouseMoveAt : function(/*String||DOMNode||Function*/ node, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Number, optional*/ offsetX, /*Number, optional*/ offsetY){ // summary: // Moves the mouse over the specified node at the specified relative x,y offset. // description: // Moves the mouse over the specified node at the specified relative x,y offset. // If you do not specify an offset, mouseMove will default to move to the middle of the node. // Example: to move the mouse over a ComboBox's down arrow node, call doh.mouseMoveAt(dijit.byId('setvaluetest').downArrowNode); // node: // The id of the node, or the node itself, to move the mouse to. // If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes. // This is useful if you need to move the mouse to an node that is not yet present. // delay: // Delay, in milliseconds, to wait before firing. // The delay is a delta with respect to the previous automation call. // For example, the following code ends after 600ms: // | robot.mouseClick({left:true}, 100) // first call; wait 100ms // | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all // duration: // Approximate time Robot will spend moving the mouse // The default is 100ms. // offsetX: // x offset relative to the node, in pixels, to move the mouse. The default is half the node's width. // offsetY: // y offset relative to the node, in pixels, to move the mouse. The default is half the node's height. robot._assertRobot(); // Schedule an action to scroll the node into view, then calculate it's center point var point = {}; this.sequence(function(){ node = robot._resolveNode(node); robot._scrollIntoView(node); var pos = robot._position(node); if(offsetY === undefined){ offsetX = pos.w/2; offsetY = pos.h/2; } point.x = pos.x+offsetX; point.y = pos.y+offsetY; }, delay); // Schedule a bunch of actions to move the mouse from the current position to point. // These actions won't run until after the above callback. this.mouseMoveTo(point, 0, duration, false); } }); return robot; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | 1 1 1 | define([ "require", "doh/main", "./aspect", "./dom-construct", "./dom-style", "./_base/kernel", "./_base/lang", "./on", "./robot", "./sniff", "./_base/window" ], function(require, doh, aspect, construct, style, kernel, lang, on, robot, has, win){ kernel.experimental("dojo.robotx"); // module: // dojo.robotx // description: // loads an external app into an iframe and points dojo.doc to the iframe document, allowing the robot to control it // to use: set robotURL in djConfig to the URL you want to load // dojo.require this file // The iframe containing the external app var iframe = null; // On IE6/7, a firebug console will appear. Scrunch it a bit to leave room for the external test file. kernel.config.debugHeight = kernel.config.debugHeight || 200; // urlLoaded is a Deferred that will be resolved whenever the iframe passed to initRobot() finishes loading, or reloads var urlLoaded; function attachIframe(url){ // summary: // Create iframe to load external app at specified url. Iframe gets onload handler to call onIframeLoad() // when specified URL finishes loading, and also if the iframe loads a different URL in the future. // returns: // A Deferred that fires when everything has finished initializing require(["./domReady!"], function(){ var emptyStyle = { overflow: "hidden", margin: "0px", borderWidth: "0px", height: "100%", width: "100%" }; style.set(document.documentElement, emptyStyle); style.set(document.body, emptyStyle); // Create the iframe for the external document. Put it above the firebug-lite div (if such a div exists). // console.log("creating iframe for external document"); iframe = document.createElement("iframe"); iframe.setAttribute("ALLOWTRANSPARENCY","true"); iframe.scrolling = has("ie") ? "yes" : "auto"; var scrollRoot = document.compatMode == "BackCompat" ? document.body : document.documentElement; var consoleHeight = (document.getElementById("firebug") || {}).offsetHeight || 0; style.set(iframe, { border: "0px none", padding: "0px", margin: "0px", width: "100%", height: consoleHeight ? (scrollRoot.clientHeight - consoleHeight)+"px" : "100%" }); iframe.src = url; // Code to handle load event on iframe. Seems like this should happen before setting iframe src on line above? // Also, can't we use on() in all cases, even for old IE? if(iframe.attachEvent !== undefined){ iframe.attachEvent("onload", onIframeLoad); }else{ on(iframe, "load", onIframeLoad); } construct.place(iframe, win.body(), "first"); }); } function onIframeLoad(){ // summary: // Load handler when iframe specified to initRobot() finishes loading, or when it reloads. // It resolves the urlLoaded Deferred to make the rests of the tests runs. robot._updateDocument(); // If dojo is present in the test case, then at least make a best effort to wait for it to load. // The test must handle other race conditions like initial data queries or asynchronous parses by itself. if(iframe.contentWindow.require){ iframe.contentWindow.require(["dojo/ready"], function(ready){ ready(Infinity, function(){ setTimeout(function(){ urlLoaded.resolve(true); }, 500); // 500ms fudge factor; otherwise focus doesn't work on IE8, see ValidationTextBox.js, TimeTextBox.js, etc. }); }); }else{ urlLoaded.resolve(true); } } lang.mixin(robot, { _updateDocument: function(){ // summary: // Called every time a new page is loaded into the iframe, to setup variables // Point dojo.global, dojo.publish, etc. to refer to iframe. // Remove for 2.0? kernel.setContext(iframe.contentWindow, iframe.contentWindow.document); // Also set pointers inside robot, for easy access via AMD (where there is no dojo variable) robot.window = iframe.contentWindow; robot.doc = iframe.contentWindow.document; // TODO: shouldn't this wait until dojo has finished loading in the iframe? See require code in onIframeLoad(). var win = kernel.global; if(win.dojo){ // allow the tests to subscribe to topics published by the iframe kernel.publish = win.dojo.publish; kernel.subscribe = win.dojo.subscribe; kernel.connectPublisher = win.dojo.connectPublisher; } }, initRobot: function(/*String*/ url){ // summary: // Opens the application at the specified URL for testing, redirecting dojo to point to the application // environment instead of the test environment. // url: // URL to open. Any of the test's dojo.doc calls (e.g. dojo.byId()), and any dijit.registry calls // (e.g. dijit.byId()) will point to elements and widgets inside this application. doh.registerGroup("initialize robot", { name: "load " + url, timeout: 100000, // could take more than 10s so setting to 100s runTest: function(){ // Setup module level urlLoaded Deferred that will be resolved by onIframeLoad(), after the iframe // has finished loading urlLoaded = new doh.Deferred(); attachIframe(url); return urlLoaded; } }); }, waitForPageToLoad: function(/*Function*/ submitActions){ // summary: // Notifies DOH that the doh.robot is about to make a page change in the application it is driving, // returning a doh.Deferred object the user should return in their runTest function as part of a DOH test. // example: // | runTest: function(){ // | return waitForPageLoad(function(){ doh.robot.keyPress(keys.ENTER, 500); }); // | } // submitActions: // The doh.robot will execute the actions the test passes into the submitActions argument (like clicking the submit button), // expecting these actions to create a page change (like a form submit). // After these actions execute and the resulting page loads, the next test will start. // Setup a new Deferred that onIframeLoad() will resolve when the iframe finishes loading urlLoaded = new doh.Deferred(); submitActions(); return urlLoaded; } }); return robot; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 1 | define([ "./router/RouterBase" ], function(RouterBase){ // module: // dojo/router /*===== return { // summary: // A singleton-style instance of dojo/router/RouterBase. See that // module for specifics. // example: // | router.register("/widgets/:id", function(evt){ // | // If "/widgets/3" was matched, // | // evt.params.id === "3" // | xhr.get({ // | url: "/some/path/" + evt.params.id, // | load: function(data){ // | // ... // | } // | }); // | }); }; =====*/ return new RouterBase({}); }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 1 | define(["./has"], function(has){ // module: // dojo/sniff /*===== return function(){ // summary: // This module sets has() flags based on the current browser. // It returns the has() function. }; =====*/ if(has("host-browser")){ var n = navigator, dua = n.userAgent, dav = n.appVersion, tv = parseFloat(dav); has.add("air", dua.indexOf("AdobeAIR") >= 0); has.add("msapp", parseFloat(dua.split("MSAppHost/")[1]) || undefined); has.add("khtml", dav.indexOf("Konqueror") >= 0 ? tv : undefined); has.add("webkit", parseFloat(dua.split("WebKit/")[1]) || undefined); has.add("chrome", parseFloat(dua.split("Chrome/")[1]) || undefined); has.add("safari", dav.indexOf("Safari")>=0 && !has("chrome") ? parseFloat(dav.split("Version/")[1]) : undefined); has.add("mac", dav.indexOf("Macintosh") >= 0); has.add("quirks", document.compatMode == "BackCompat"); if(dua.match(/(iPhone|iPod|iPad)/)){ var p = RegExp.$1.replace(/P/, "p"); var v = dua.match(/OS ([\d_]+)/) ? RegExp.$1 : "1"; var os = parseFloat(v.replace(/_/, ".").replace(/_/g, "")); has.add(p, os); // "iphone", "ipad" or "ipod" has.add("ios", os); } has.add("android", parseFloat(dua.split("Android ")[1]) || undefined); has.add("bb", (dua.indexOf("BlackBerry") >= 0 || dua.indexOf("BB10") >= 0) && parseFloat(dua.split("Version/")[1]) || undefined); has.add("trident", parseFloat(dav.split("Trident/")[1]) || undefined); has.add("svg", typeof SVGAngle !== "undefined"); if(!has("webkit")){ // Opera if(dua.indexOf("Opera") >= 0){ // see http://dev.opera.com/articles/view/opera-ua-string-changes and http://www.useragentstring.com/pages/Opera/ // 9.8 has both styles; <9.8, 9.9 only old style has.add("opera", tv >= 9.8 ? parseFloat(dua.split("Version/")[1]) || tv : tv); } // Mozilla and firefox if(dua.indexOf("Gecko") >= 0 && !has("khtml") && !has("webkit") && !has("trident")){ has.add("mozilla", tv); } if(has("mozilla")){ //We really need to get away from this. Consider a sane isGecko approach for the future. has.add("ff", parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1]) || undefined); } // IE if(document.all && !has("opera")){ var isIE = parseFloat(dav.split("MSIE ")[1]) || undefined; //In cases where the page has an HTTP header or META tag with //X-UA-Compatible, then it is in emulation mode. //Make sure isIE reflects the desired version. //document.documentMode of 5 means quirks mode. //Only switch the value if documentMode's major version //is different from isIE's major version. var mode = document.documentMode; if(mode && mode != 5 && Math.floor(isIE) != mode){ isIE = mode; } has.add("ie", isIE); } // Wii has.add("wii", typeof opera != "undefined" && opera.wiiremote); } } return has; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 1 | define([ "./_base/kernel", // kernel.global "./_base/lang" ], function(kernel, lang){ // module: // dojo/string var string = { // summary: // String utilities for Dojo }; lang.setObject("dojo.string", string); string.rep = function(/*String*/str, /*Integer*/num){ // summary: // Efficiently replicate a string `n` times. // str: // the string to replicate // num: // number of times to replicate the string if(num <= 0 || !str){ return ""; } var buf = []; for(;;){ if(num & 1){ buf.push(str); } if(!(num >>= 1)){ break; } str += str; } return buf.join(""); // String }; string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){ // summary: // Pad a string to guarantee that it is at least `size` length by // filling with the character `ch` at either the start or end of the // string. Pads at the start, by default. // text: // the string to pad // size: // length to provide padding // ch: // character to pad, defaults to '0' // end: // adds padding at the end if true, otherwise pads at start // example: // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++". // | string.pad("Dojo", 10, "+", true); if(!ch){ ch = '0'; } var out = String(text), pad = string.rep(ch, Math.ceil((size - out.length) / ch.length)); return end ? out + pad : pad + out; // String }; string.substitute = function( /*String*/ template, /*Object|Array*/map, /*Function?*/ transform, /*Object?*/ thisObject){ // summary: // Performs parameterized substitutions on a string. Throws an // exception if any parameter is unmatched. // template: // a string with expressions in the form `${key}` to be replaced or // `${key:format}` which specifies a format function. keys are case-sensitive. // map: // hash to search for substitutions // transform: // a function to process all parameters before substitution takes // place, e.g. mylib.encodeXML // thisObject: // where to look for optional format function; default to the global // namespace // example: // Substitutes two expressions in a string from an Array or Object // | // returns "File 'foo.html' is not found in directory '/temp'." // | // by providing substitution data in an Array // | string.substitute( // | "File '${0}' is not found in directory '${1}'.", // | ["foo.html","/temp"] // | ); // | // | // also returns "File 'foo.html' is not found in directory '/temp'." // | // but provides substitution data in an Object structure. Dotted // | // notation may be used to traverse the structure. // | string.substitute( // | "File '${name}' is not found in directory '${info.dir}'.", // | { name: "foo.html", info: { dir: "/temp" } } // | ); // example: // Use a transform function to modify the values: // | // returns "file 'foo.html' is not found in directory '/temp'." // | string.substitute( // | "${0} is not found in ${1}.", // | ["foo.html","/temp"], // | function(str){ // | // try to figure out the type // | var prefix = (str.charAt(0) == "/") ? "directory": "file"; // | return prefix + " '" + str + "'"; // | } // | ); // example: // Use a formatter // | // returns "thinger -- howdy" // | string.substitute( // | "${0:postfix}", ["thinger"], null, { // | postfix: function(value, key){ // | return value + " -- howdy"; // | } // | } // | ); thisObject = thisObject || kernel.global; transform = transform ? lang.hitch(thisObject, transform) : function(v){ return v; }; return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, function(match, key, format){ var value = lang.getObject(key, false, map); if(format){ value = lang.getObject(format, false, thisObject).call(thisObject, value, key); } return transform(value, key).toString(); }); // String }; string.trim = String.prototype.trim ? lang.trim : // aliasing to the native function function(str){ str = str.replace(/^\s+/, ''); for(var i = str.length - 1; i >= 0; i--){ if(/\S/.test(str.charAt(i))){ str = str.substring(0, i + 1); break; } } return str; }; /*===== string.trim = function(str){ // summary: // Trims whitespace from both sides of the string // str: String // String to be trimmed // returns: String // Returns the trimmed string // description: // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript). // The short yet performant version of this function is dojo/_base/lang.trim(), // which is part of Dojo base. Uses String.prototype.trim instead, if available. return ""; // String }; =====*/ return string; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | 1 | define(["./_base/kernel", "require", "./has", "./has!host-browser?./request"], function(dojo, require, has, request){ // module: // dojo/text var getText; if(has("host-browser")){ getText= function(url, sync, load){ request(url, {sync:!!sync}).then(load); }; }else{ // Path for node.js and rhino, to load from local file system. // TODO: use node.js native methods rather than depending on a require.getText() method to exist. if(require.getText){ getText= require.getText; }else{ console.error("dojo/text plugin failed to load because loader does not support getText"); } } var theCache = {}, strip= function(text){ //Strips <?xml ...?> declarations so that external SVG and XML //documents can be added to a document without worry. Also, if the string //is an HTML document, only the part inside the body tag is returned. if(text){ text= text.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, ""); var matches= text.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); if(matches){ text= matches[1]; } }else{ text = ""; } return text; }, notFound = {}, pending = {}; dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){ // summary: // A getter and setter for storing the string content associated with the // module and url arguments. // description: // If module is a string that contains slashes, then it is interpretted as a fully // resolved path (typically a result returned by require.toUrl), and url should not be // provided. This is the preferred signature. If module is a string that does not // contain slashes, then url must also be provided and module and url are used to // call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated. // If value is specified, the cache value for the moduleUrl will be set to // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it // in its internal cache and return that cached value for the URL. To clear // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the // the URL contents, only modules on the same domain of the page can use this capability. // The build system can inline the cache values though, to allow for xdomain hosting. // module: String||Object // If a String with slashes, a fully resolved path; if a String without slashes, the // module name to use for the base part of the URL, similar to module argument // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that // generates a valid path for the cache item. For example, a dojo._Url object. // url: String // The rest of the path to append to the path derived from the module argument. If // module is an object, then this second argument should be the "value" argument instead. // value: String||Object? // If a String, the value to use in the cache for the module/url combination. // If an Object, it can have two properties: value and sanitize. The value property // should be the value to use in the cache, and sanitize can be set to true or false, // to indicate if XML declarations should be removed from the value and if the HTML // inside a body tag in the value should be extracted as the real value. The value argument // or the value property on the value argument are usually only used by the build system // as it inlines cache content. // example: // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style // of call is used to avoid an issue with the build system erroneously trying to intern // this example. To get the build system to intern your dojo.cache calls, use the // "dojo.cache" style of call): // | //If template.html contains "<h1>Hello</h1>" that will be // | //the value for the text variable. // | //Note: This is pre-AMD, deprecated syntax // | var text = dojo["cache"]("my.module", "template.html"); // example: // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input // (the dojo["cache"] style of call is used to avoid an issue with the build system // erroneously trying to intern this example. To get the build system to intern your // dojo.cache calls, use the "dojo.cache" style of call): // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the // | //text variable will contain just "<h1>Hello</h1>". // | //Note: This is pre-AMD, deprecated syntax // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true}); // example: // Same example as previous, but demonstrates how an object can be passed in as // the first argument, then the value argument can then be the second argument. // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the // | //text variable will contain just "<h1>Hello</h1>". // | //Note: This is pre-AMD, deprecated syntax // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true}); // * (string string [value]) => (module, url, value) // * (object [value]) => (module, value), url defaults to "" // // * if module is an object, then it must be convertable to a string // * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl // * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize" var key; if(typeof module=="string"){ if(/\//.test(module)){ // module is a version 1.7+ resolved path key = module; value = url; }else{ // module is a version 1.6- argument to dojo.moduleUrl key = require.toUrl(module.replace(/\./g, "/") + (url ? ("/" + url) : "")); } }else{ key = module + ""; value = url; } var val = (value != undefined && typeof value != "string") ? value.value : value, sanitize = value && value.sanitize; if(typeof val == "string"){ //We have a string, set cache value theCache[key] = val; return sanitize ? strip(val) : val; }else if(val === null){ //Remove cached value delete theCache[key]; return null; }else{ //Allow cache values to be empty strings. If key property does //not exist, fetch it. if(!(key in theCache)){ getText(key, true, function(text){ theCache[key]= text; }); } return sanitize ? strip(theCache[key]) : theCache[key]; } }; return { // summary: // This module implements the dojo/text! plugin and the dojo.cache API. // description: // We choose to include our own plugin to leverage functionality already contained in dojo // and thereby reduce the size of the plugin compared to various foreign loader implementations. // Also, this allows foreign AMD loaders to be used without their plugins. // // CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous // loader. This feature is outside the scope of the CommonJS plugins specification. // the dojo/text caches it's own resources because of dojo.cache dynamic: true, normalize: function(id, toAbsMid){ // id is something like (path may be relative): // // "path/to/text.html" // "path/to/text.html!strip" var parts= id.split("!"), url= parts[0]; return (/^\./.test(url) ? toAbsMid(url) : url) + (parts[1] ? "!" + parts[1] : ""); }, load: function(id, require, load){ // id: String // Path to the resource. // require: Function // Object that include the function toUrl with given id returns a valid URL from which to load the text. // load: Function // Callback function which will be called, when the loading finished. // id is something like (path is always absolute): // // "path/to/text.html" // "path/to/text.html!strip" var parts= id.split("!"), stripFlag= parts.length>1, absMid= parts[0], url = require.toUrl(parts[0]), requireCacheUrl = "url:" + url, text = notFound, finish = function(text){ load(stripFlag ? strip(text) : text); }; if(absMid in theCache){ text = theCache[absMid]; }else if(require.cache && requireCacheUrl in require.cache){ text = require.cache[requireCacheUrl]; }else if(url in theCache){ text = theCache[url]; } if(text===notFound){ if(pending[url]){ pending[url].push(finish); }else{ var pendingList = pending[url] = [finish]; getText(url, !require.async, function(text){ theCache[absMid]= theCache[url]= text; for(var i = 0; i<pendingList.length;){ pendingList[i++](text); } delete pending[url]; }); } }else{ finish(text); } } }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 1 | define(["./Evented"], function(Evented){ // module: // dojo/topic var hub = new Evented; return { // summary: // Pubsub hub. // example: // | topic.subscribe("some/topic", function(event){ // | ... do something with event // | }); // | topic.publish("some/topic", {name:"some event", ...}); publish: function(topic, event){ // summary: // Publishes a message to a topic on the pub/sub hub. All arguments after // the first will be passed to the subscribers, so any number of arguments // can be provided (not just event). // topic: String // The name of the topic to publish to // event: Object // An event to distribute to the topic listeners return hub.emit.apply(hub, arguments); }, subscribe: function(topic, listener){ // summary: // Subscribes to a topic on the pub/sub hub // topic: String // The topic to subscribe to // listener: Function // A function to call when a message is published to the given topic return hub.on.apply(hub, arguments); } }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 | 1 1 1 1 1 1 | define(["./_base/kernel", "./aspect", "./dom", "./dom-class", "./_base/lang", "./on", "./has", "./mouse", "./domReady", "./_base/window"], function(dojo, aspect, dom, domClass, lang, on, has, mouse, domReady, win){ // module: // dojo/touch var hasTouch = has("touch"); var ios4 = has("ios") < 5; var msPointer = navigator.pointerEnabled || navigator.msPointerEnabled, pointer = (function () { var pointer = {}; for (var type in { down: 1, move: 1, up: 1, cancel: 1, over: 1, out: 1 }) { pointer[type] = !navigator.pointerEnabled ? "MSPointer" + type.charAt(0).toUpperCase() + type.slice(1) : "pointer" + type; } return pointer; })(); // Click generation variables var clicksInited, clickTracker, clickTarget, clickX, clickY, clickDx, clickDy, clickTime; // Time of most recent touchstart, touchmove, or touchend event var lastTouch; function dualEvent(mouseType, touchType, msPointerType){ // Returns synthetic event that listens for both the specified mouse event and specified touch event. // But ignore fake mouse events that were generated due to the user touching the screen. if(msPointer && msPointerType){ // IE10+: MSPointer* events are designed to handle both mouse and touch in a uniform way, // so just use that regardless of hasTouch. return function(node, listener){ return on(node, msPointerType, listener); } }else if(hasTouch){ return function(node, listener){ var handle1 = on(node, touchType, listener), handle2 = on(node, mouseType, function(evt){ if(!lastTouch || (new Date()).getTime() > lastTouch + 1000){ listener.call(this, evt); } }); return { remove: function(){ handle1.remove(); handle2.remove(); } }; }; }else{ // Avoid creating listeners for touch events on performance sensitive older browsers like IE6 return function(node, listener){ return on(node, mouseType, listener); } } } function marked(/*DOMNode*/ node){ // Test if a node or its ancestor has been marked with the dojoClick property to indicate special processing, do{ if(node.dojoClick !== undefined){ return node.dojoClick; } }while(node = node.parentNode); } function doClicks(e, moveType, endType){ // summary: // Setup touch listeners to generate synthetic clicks immediately (rather than waiting for the browser // to generate clicks after the double-tap delay) and consistently (regardless of whether event.preventDefault() // was called in an event listener. Synthetic clicks are generated only if a node or one of its ancestors has // its dojoClick property set to truthy. If a node receives synthetic clicks because one of its ancestors has its // dojoClick property set to truthy, you can disable synthetic clicks on this node by setting its own dojoClick property // to falsy. clickTracker = !e.target.disabled && marked(e.target); // click threshold = true, number or x/y object if(clickTracker){ clickTarget = e.target; clickX = e.touches ? e.touches[0].pageX : e.clientX; clickY = e.touches ? e.touches[0].pageY : e.clientY; clickDx = (typeof clickTracker == "object" ? clickTracker.x : (typeof clickTracker == "number" ? clickTracker : 0)) || 4; clickDy = (typeof clickTracker == "object" ? clickTracker.y : (typeof clickTracker == "number" ? clickTracker : 0)) || 4; // add move/end handlers only the first time a node with dojoClick is seen, // so we don't add too much overhead when dojoClick is never set. if(!clicksInited){ clicksInited = true; win.doc.addEventListener(moveType, function(e){ clickTracker = clickTracker && e.target == clickTarget && Math.abs((e.touches ? e.touches[0].pageX : e.clientX) - clickX) <= clickDx && Math.abs((e.touches ? e.touches[0].pageY : e.clientY) - clickY) <= clickDy; }, true); win.doc.addEventListener(endType, function(e){ if(clickTracker){ clickTime = (new Date()).getTime(); var target = e.target; if(target.tagName === "LABEL"){ // when clicking on a label, forward click to its associated input if any target = dom.byId(target.getAttribute("for")) || target; } setTimeout(function(){ on.emit(target, "click", { bubbles : true, cancelable : true, _dojo_click : true }); }); } }, true); function stopNativeEvents(type){ win.doc.addEventListener(type, function(e){ // Stop native events when we emitted our own click event. Note that the native click may occur // on a different node than the synthetic click event was generated on. For example, // click on a menu item, causing the menu to disappear, and then (~300ms later) the browser // sends a click event to the node that was *underneath* the menu. So stop all native events // sent shortly after ours, similar to what is done in dualEvent. // The INPUT.dijitOffScreen test is for offscreen inputs used in dijit/form/Button, on which // we call click() explicitly, we don't want to stop this event. if(!e._dojo_click && (new Date()).getTime() <= clickTime + 1000 && !(e.target.tagName == "INPUT" && domClass.contains(e.target, "dijitOffScreen"))){ e.stopPropagation(); e.stopImmediatePropagation && e.stopImmediatePropagation(); if(type == "click" && (e.target.tagName != "INPUT" || e.target.type == "radio" || e.target.type == "checkbox") && e.target.tagName != "TEXTAREA" && e.target.tagName != "AUDIO" && e.target.tagName != "VIDEO"){ // preventDefault() breaks textual <input>s on android, keyboard doesn't popup, // but it is still needed for checkboxes and radio buttons, otherwise in some cases // the checked state becomes inconsistent with the widget's state e.preventDefault(); } } }, true); } stopNativeEvents("click"); // We also stop mousedown/up since these would be sent well after with our "fast" click (300ms), // which can confuse some dijit widgets. stopNativeEvents("mousedown"); stopNativeEvents("mouseup"); } } } var hoveredNode; if(hasTouch){ if(msPointer){ // MSPointer (IE10+) already has support for over and out, so we just need to init click support domReady(function(){ win.doc.addEventListener(pointer.down, function(evt){ doClicks(evt, pointer.move, pointer.up); }, true); }); }else{ domReady(function(){ // Keep track of currently hovered node hoveredNode = win.body(); // currently hovered node win.doc.addEventListener("touchstart", function(evt){ lastTouch = (new Date()).getTime(); // Precede touchstart event with touch.over event. DnD depends on this. // Use addEventListener(cb, true) to run cb before any touchstart handlers on node run, // and to ensure this code runs even if the listener on the node does event.stop(). var oldNode = hoveredNode; hoveredNode = evt.target; on.emit(oldNode, "dojotouchout", { relatedTarget: hoveredNode, bubbles: true }); on.emit(hoveredNode, "dojotouchover", { relatedTarget: oldNode, bubbles: true }); doClicks(evt, "touchmove", "touchend"); // init click generation }, true); function copyEventProps(evt){ // Make copy of event object and also set bubbles:true. Used when calling on.emit(). var props = lang.delegate(evt, { bubbles: true }); if(has("ios") >= 6){ // On iOS6 "touches" became a non-enumerable property, which // is not hit by for...in. Ditto for the other properties below. props.touches = evt.touches; props.altKey = evt.altKey; props.changedTouches = evt.changedTouches; props.ctrlKey = evt.ctrlKey; props.metaKey = evt.metaKey; props.shiftKey = evt.shiftKey; props.targetTouches = evt.targetTouches; } return props; } on(win.doc, "touchmove", function(evt){ lastTouch = (new Date()).getTime(); var newNode = win.doc.elementFromPoint( evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords evt.pageY - (ios4 ? 0 : win.global.pageYOffset) ); if(newNode){ // Fire synthetic touchover and touchout events on nodes since the browser won't do it natively. if(hoveredNode !== newNode){ // touch out on the old node on.emit(hoveredNode, "dojotouchout", { relatedTarget: newNode, bubbles: true }); // touchover on the new node on.emit(newNode, "dojotouchover", { relatedTarget: hoveredNode, bubbles: true }); hoveredNode = newNode; } // Unlike a listener on "touchmove", on(node, "dojotouchmove", listener) fires when the finger // drags over the specified node, regardless of which node the touch started on. if(!on.emit(newNode, "dojotouchmove", copyEventProps(evt))){ // emit returns false when synthetic event "dojotouchmove" is cancelled, so we prevent the // default behavior of the underlying native event "touchmove". evt.preventDefault(); } } }); // Fire a dojotouchend event on the node where the finger was before it was removed from the screen. // This is different than the native touchend, which fires on the node where the drag started. on(win.doc, "touchend", function(evt){ lastTouch = (new Date()).getTime(); var node = win.doc.elementFromPoint( evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords evt.pageY - (ios4 ? 0 : win.global.pageYOffset) ) || win.body(); // if out of the screen on.emit(node, "dojotouchend", copyEventProps(evt)); }); }); } } //device neutral events - touch.press|move|release|cancel/over/out var touch = { press: dualEvent("mousedown", "touchstart", pointer.down), move: dualEvent("mousemove", "dojotouchmove", pointer.move), release: dualEvent("mouseup", "dojotouchend", pointer.up), cancel: dualEvent(mouse.leave, "touchcancel", hasTouch ? pointer.cancel : null), over: dualEvent("mouseover", "dojotouchover", pointer.over), out: dualEvent("mouseout", "dojotouchout", pointer.out), enter: mouse._eventHandler(dualEvent("mouseover","dojotouchover", pointer.over)), leave: mouse._eventHandler(dualEvent("mouseout", "dojotouchout", pointer.out)) }; /*===== touch = { // summary: // This module provides unified touch event handlers by exporting // press, move, release and cancel which can also run well on desktop. // Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html // Also, if the dojoClick property is set to truthy on a DOM node, dojo/touch generates // click events immediately for this node and its descendants (except for descendants that // have a dojoClick property set to falsy), to avoid the delay before native browser click events, // and regardless of whether evt.preventDefault() was called in a touch.press event listener. // // example: // Used with dojo/on // | define(["dojo/on", "dojo/touch"], function(on, touch){ // | on(node, touch.press, function(e){}); // | on(node, touch.move, function(e){}); // | on(node, touch.release, function(e){}); // | on(node, touch.cancel, function(e){}); // example: // Used with touch.* directly // | touch.press(node, function(e){}); // | touch.move(node, function(e){}); // | touch.release(node, function(e){}); // | touch.cancel(node, function(e){}); // example: // Have dojo/touch generate clicks without delay, with a default move threshold of 4 pixels // | node.dojoClick = true; // example: // Have dojo/touch generate clicks without delay, with a move threshold of 10 pixels horizontally and vertically // | node.dojoClick = 10; // example: // Have dojo/touch generate clicks without delay, with a move threshold of 50 pixels horizontally and 10 pixels vertically // | node.dojoClick = {x:50, y:5}; // example: // Disable clicks without delay generated by dojo/touch on a node that has an ancestor with property dojoClick set to truthy // | node.dojoClick = false; press: function(node, listener){ // summary: // Register a listener to 'touchstart'|'mousedown' for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, move: function(node, listener){ // summary: // Register a listener that fires when the mouse cursor or a finger is dragged over the given node. // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, release: function(node, listener){ // summary: // Register a listener to releasing the mouse button while the cursor is over the given node // (i.e. "mouseup") or for removing the finger from the screen while touching the given node. // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, cancel: function(node, listener){ // summary: // Register a listener to 'touchcancel'|'mouseleave' for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, over: function(node, listener){ // summary: // Register a listener to 'mouseover' or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, out: function(node, listener){ // summary: // Register a listener to 'mouseout' or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, enter: function(node, listener){ // summary: // Register a listener to mouse.enter or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() }, leave: function(node, listener){ // summary: // Register a listener to mouse.leave or touch equivalent for the given node // node: Dom // Target node to listen to // listener: Function // Callback function // returns: // A handle which will be used to remove the listener by handle.remove() } }; =====*/ has("extend-dojo") && (dojo.touch = touch); return touch; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | 1 | define(["./dom-geometry", "./_base/lang", "./domReady", "./sniff", "./_base/window"], function(geometry, lang, domReady, has, baseWindow){ // module: // dojo/uacss /*===== return { // summary: // Applies pre-set CSS classes to the top-level HTML node, based on: // // - browser (ex: dj_ie) // - browser version (ex: dj_ie6) // - box model (ex: dj_contentBox) // - text direction (ex: dijitRtl) // // In addition, browser, browser version, and box model are // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl. // // Returns the has() method. }; =====*/ var html = baseWindow.doc.documentElement, ie = has("ie"), opera = has("opera"), maj = Math.floor, ff = has("ff"), boxModel = geometry.boxModel.replace(/-/,''), classes = { "dj_quirks": has("quirks"), // NOTE: Opera not supported by dijit "dj_opera": opera, "dj_khtml": has("khtml"), "dj_webkit": has("webkit"), "dj_safari": has("safari"), "dj_chrome": has("chrome"), "dj_gecko": has("mozilla"), "dj_ios": has("ios"), "dj_android": has("android") }; // no dojo unsupported browsers if(ie){ classes["dj_ie"] = true; classes["dj_ie" + maj(ie)] = true; classes["dj_iequirks"] = has("quirks"); } if(ff){ classes["dj_ff" + maj(ff)] = true; } classes["dj_" + boxModel] = true; // apply browser, browser version, and box model class names var classStr = ""; for(var clz in classes){ if(classes[clz]){ classStr += clz + " "; } } html.className = lang.trim(html.className + " " + classStr); // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension. // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl). domReady(function(){ if(!geometry.isBodyLtr()){ var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl "); html.className = lang.trim(html.className + " " + rtlClassStr + "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ")); } }); return has; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | 1 | define([ "./Deferred", "./promise/Promise" ], function(Deferred, Promise){ "use strict"; // module: // dojo/when return function when(valueOrPromise, callback, errback, progback){ // summary: // Transparently applies callbacks to values and/or promises. // description: // Accepts promises but also transparently handles non-promises. If no // callbacks are provided returns a promise, regardless of the initial // value. Foreign promises are converted. // // If callbacks are provided and the initial value is not a promise, // the callback is executed immediately with no error handling. Returns // a promise if the initial value is a promise, or the result of the // callback otherwise. // valueOrPromise: // Either a regular value or an object with a `then()` method that // follows the Promises/A specification. // callback: Function? // Callback to be invoked when the promise is resolved, or a non-promise // is received. // errback: Function? // Callback to be invoked when the promise is rejected. // progback: Function? // Callback to be invoked when the promise emits a progress update. // returns: dojo/promise/Promise // Promise, or if a callback is provided, the result of the callback. var receivedPromise = valueOrPromise && typeof valueOrPromise.then === "function"; var nativePromise = receivedPromise && valueOrPromise instanceof Promise; if(!receivedPromise){ if(arguments.length > 1){ return callback ? callback(valueOrPromise) : valueOrPromise; }else{ return new Deferred().resolve(valueOrPromise); } }else if(!nativePromise){ var deferred = new Deferred(valueOrPromise.cancel); valueOrPromise.then(deferred.resolve, deferred.reject, deferred.progress); valueOrPromise = deferred.promise; } if(callback || errback || progback){ return valueOrPromise.then(callback, errback, progback); } return valueOrPromise; }; }); |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | 1 | define(["./_base/lang", "./sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style", "./dom-construct"], function(lang, has, baseWindow, dom, geom, style, domConstruct){ // feature detection /* not needed but included here for future reference has.add("rtl-innerVerticalScrollBar-on-left", function(win, doc){ var body = baseWindow.body(doc), scrollable = domConstruct.create('div', { style: {overflow:'scroll', overflowX:'hidden', direction:'rtl', visibility:'hidden', position:'absolute', left:'0', width:'64px', height:'64px'} }, body, "last"), center = domConstruct.create('center', { style: {overflow:'hidden', direction:'ltr'} }, scrollable, "last"), inner = domConstruct.create('div', { style: {overflow:'visible', display:'inline' } }, center, "last"); inner.innerHTML=" "; var midPoint = Math.max(inner.offsetLeft, geom.position(inner).x); var ret = midPoint >= 32; center.removeChild(inner); scrollable.removeChild(center); body.removeChild(scrollable); return ret; }); */ has.add("rtl-adjust-position-for-verticalScrollBar", function(win, doc){ var body = baseWindow.body(doc), scrollable = domConstruct.create('div', { style: {overflow:'scroll', overflowX:'visible', direction:'rtl', visibility:'hidden', position:'absolute', left:'0', top:'0', width:'64px', height:'64px'} }, body, "last"), div = domConstruct.create('div', { style: {overflow:'hidden', direction:'ltr'} }, scrollable, "last"), ret = geom.position(div).x != 0; scrollable.removeChild(div); body.removeChild(scrollable); return ret; }); has.add("position-fixed-support", function(win, doc){ // IE6, IE7+quirks, and some older mobile browsers don't support position:fixed var body = baseWindow.body(doc), outer = domConstruct.create('span', { style: {visibility:'hidden', position:'fixed', left:'1px', top:'1px'} }, body, "last"), inner = domConstruct.create('span', { style: {position:'fixed', left:'0', top:'0'} }, outer, "last"), ret = geom.position(inner).x != geom.position(outer).x; outer.removeChild(inner); body.removeChild(outer); return ret; }); // module: // dojo/window var window = { // summary: // TODOC getBox: function(/*Document?*/ doc){ // summary: // Returns the dimensions and scroll position of the viewable area of a browser window doc = doc || baseWindow.doc; var scrollRoot = (doc.compatMode == 'BackCompat') ? baseWindow.body(doc) : doc.documentElement, // get scroll position scroll = geom.docScroll(doc), // scrollRoot.scrollTop/Left should work w, h; if(has("touch")){ // if(scrollbars not supported) var uiWindow = window.get(doc); // use UI window, not dojo.global window // on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight w = uiWindow.innerWidth || scrollRoot.clientWidth; // || scrollRoot.clientXXX probably never evaluated h = uiWindow.innerHeight || scrollRoot.clientHeight; }else{ // on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight // uiWindow.innerWidth/Height includes the scrollbar and cannot be used w = scrollRoot.clientWidth; h = scrollRoot.clientHeight; } return { l: scroll.x, t: scroll.y, w: w, h: h }; }, get: function(/*Document*/ doc){ // summary: // Get window object associated with document doc. // doc: // The document to get the associated window for. // In some IE versions (at least 6.0), document.parentWindow does not return a // reference to the real window object (maybe a copy), so we must fix it as well // We use IE specific execScript to attach the real window reference to // document._parentWindow for later use if(has("ie") && window !== document.parentWindow){ /* In IE 6, only the variable "window" can be used to connect events (others may be only copies). */ doc.parentWindow.execScript("document._parentWindow = window;", "Javascript"); //to prevent memory leak, unset it after use //another possibility is to add an onUnload handler which seems overkill to me (liucougar) var win = doc._parentWindow; doc._parentWindow = null; return win; // Window } return doc.parentWindow || doc.defaultView; // Window }, scrollIntoView: function(/*DomNode*/ node, /*Object?*/ pos){ // summary: // Scroll the passed node into view using minimal movement, if it is not already. // Don't rely on node.scrollIntoView working just because the function is there since // it forces the node to the page's bottom or top (and left or right in IE) without consideration for the minimal movement. // WebKit's node.scrollIntoViewIfNeeded doesn't work either for inner scrollbars in right-to-left mode // and when there's a fixed position scrollable element try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method node = dom.byId(node); var doc = node.ownerDocument || baseWindow.doc, // TODO: why baseWindow.doc? Isn't node.ownerDocument always defined? body = baseWindow.body(doc), html = doc.documentElement || body.parentNode, isIE = has("ie"), isWK = has("webkit"); // if an untested browser, then use the native method if(node == body || node == html){ return; } if(!(has("mozilla") || isIE || isWK || has("opera") || has("trident")) && ("scrollIntoView" in node)){ node.scrollIntoView(false); // short-circuit to native if possible return; } var backCompat = doc.compatMode == 'BackCompat', rootWidth = Math.min(body.clientWidth || html.clientWidth, html.clientWidth || body.clientWidth), rootHeight = Math.min(body.clientHeight || html.clientHeight, html.clientHeight || body.clientHeight), scrollRoot = (isWK || backCompat) ? body : html, nodePos = pos || geom.position(node), el = node.parentNode, isFixed = function(el){ return (isIE <= 6 || (isIE == 7 && backCompat)) ? false : (has("position-fixed-support") && (style.get(el, 'position').toLowerCase() == "fixed")); }; if(isFixed(node)){ return; } // nothing to do while(el){ if(el == body){ el = scrollRoot; } var elPos = geom.position(el), fixedPos = isFixed(el), rtl = style.getComputedStyle(el).direction.toLowerCase() == "rtl"; if(el == scrollRoot){ elPos.w = rootWidth; elPos.h = rootHeight; if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x if(elPos.x < 0 || !isIE || isIE >= 9){ elPos.x = 0; } // older IE can have values > 0 if(elPos.y < 0 || !isIE || isIE >= 9){ elPos.y = 0; } }else{ var pb = geom.getPadBorderExtents(el); elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t; var clientSize = el.clientWidth, scrollBarSize = elPos.w - clientSize; if(clientSize > 0 && scrollBarSize > 0){ if(rtl && has("rtl-adjust-position-for-verticalScrollBar")){ elPos.x += scrollBarSize; } elPos.w = clientSize; } clientSize = el.clientHeight; scrollBarSize = elPos.h - clientSize; if(clientSize > 0 && scrollBarSize > 0){ elPos.h = clientSize; } } if(fixedPos){ // bounded by viewport, not parents if(elPos.y < 0){ elPos.h += elPos.y; elPos.y = 0; } if(elPos.x < 0){ elPos.w += elPos.x; elPos.x = 0; } if(elPos.y + elPos.h > rootHeight){ elPos.h = rootHeight - elPos.y; } if(elPos.x + elPos.w > rootWidth){ elPos.w = rootWidth - elPos.x; } } // calculate overflow in all 4 directions var l = nodePos.x - elPos.x, // beyond left: < 0 // t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0 t = nodePos.y - elPos.y, // beyond top: < 0 r = l + nodePos.w - elPos.w, // beyond right: > 0 bot = t + nodePos.h - elPos.h; // beyond bottom: > 0 var s, old; if(r * l > 0 && (!!el.scrollLeft || el == scrollRoot || el.scrollWidth > el.offsetHeight)){ s = Math[l < 0? "max" : "min"](l, r); if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; } old = el.scrollLeft; el.scrollLeft += s; s = el.scrollLeft - old; nodePos.x -= s; } if(bot * t > 0 && (!!el.scrollTop || el == scrollRoot || el.scrollHeight > el.offsetHeight)){ s = Math.ceil(Math[t < 0? "max" : "min"](t, bot)); old = el.scrollTop; el.scrollTop += s; s = el.scrollTop - old; nodePos.y -= s; } el = (el != scrollRoot) && !fixedPos && el.parentNode; } }catch(error){ console.error('scrollIntoView: ' + error); node.scrollIntoView(false); } } }; has("extend-dojo") && lang.setObject("dojo.window", window); return window; }); |
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| configNode.js | 58.33% | (14 / 24) | 25% | (1 / 4) | 40% | (2 / 5) | 58.33% | (14 / 24) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 1 2 4 4 4 2 2 2 28 2 2 2 2 12 | exports.config = function(config){
// summary:
// This module provides bootstrap configuration for running dojo in node.js
// any command line arguments with the load flag are pushed into deps
for(var deps = [], args = [], i = 0; i < process.argv.length; i++){
var arg = (process.argv[i] + "").split("=");
Iif(arg[0] == "load"){
deps.push(arg[1]);
}else{
args.push(arg);
}
}
var fs = require("fs");
// make sure global require exists
//if (typeof global.require=="undefined"){
// global.require= {};
//}
// reset the has cache with node-appropriate values;
var hasCache = {
"host-node":1,
"host-browser":0,
"dom":0,
"dojo-has-api":1,
"dojo-xhr-factory":0,
"dojo-inject-api":1,
"dojo-timeout-api":0,
"dojo-trace-api":1,
"dojo-dom-ready-api":0,
"dojo-publish-privates":1,
"dojo-sniff":0,
"dojo-loader":1,
"dojo-test-xd":0,
"dojo-test-sniff":0
};
for(var p in hasCache){
config.hasCache[p] = hasCache[p];
}
var vm = require('vm'),
path = require('path');
// reset some configuration switches with node-appropriate values
var nodeConfig = {
baseUrl: path.dirname(process.argv[1]),
commandLineArgs:args,
deps:deps,
timeout:0,
// TODO: really get the locale
locale:"en-us",
loaderPatch: {
log:function(item){
console.dir(item.stack || item);
// define debug for console messages during dev instead of console.log
// (node's heavy async makes console.log confusing sometimes)
var util = require("util");
util.debug(util.inspect(item));
},
eval: function(__text, __urlHint){
return vm.runInThisContext(__text, __urlHint);
},
injectUrl: function(url, callback){
try{
vm.runInThisContext(fs.readFileSync(url, "utf8"), url);
callback();
}catch(e){
this.log("failed to load resource (" + url + ")");
this.log(e);
}
},
getText: function(url, sync, onLoad){
// TODO: implement async and http/https handling
onLoad(fs.readFileSync(url, "utf8"));
}
}
};
for(p in nodeConfig){
config[p] = nodeConfig[p];
}
};
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| lodash.js | 30.29% | (597 / 1971) | 9.52% | (162 / 1701) | 11.48% | (31 / 270) | 30.25% | (595 / 1967) |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 6722 6723 6724 6725 6726 6727 6728 6729 6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 6744 6745 6746 6747 6748 6749 6750 6751 6752 6753 6754 6755 6756 6757 6758 6759 6760 6761 6762 6763 6764 6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 6796 6797 6798 6799 6800 6801 6802 6803 6804 6805 6806 6807 6808 6809 6810 6811 6812 6813 6814 6815 6816 6817 6818 6819 6820 6821 6822 6823 6824 6825 6826 6827 6828 6829 6830 6831 6832 6833 6834 6835 6836 6837 6838 6839 6840 6841 6842 6843 6844 6845 6846 6847 6848 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967 6968 6969 6970 6971 6972 6973 6974 6975 6976 6977 6978 6979 6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022 7023 7024 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 7047 7048 7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 7063 7064 7065 7066 7067 7068 7069 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 7122 7123 7124 7125 7126 7127 7128 7129 7130 7131 7132 7133 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 7149 7150 7151 7152 7153 7154 7155 7156 7157 7158 7159 7160 7161 7162 7163 7164 7165 7166 7167 7168 7169 7170 7171 7172 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 7223 7224 7225 7226 7227 7228 7229 7230 7231 7232 7233 7234 7235 7236 7237 7238 7239 7240 7241 7242 7243 7244 7245 7246 7247 7248 7249 7250 7251 7252 7253 7254 7255 7256 7257 7258 7259 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 7282 7283 7284 7285 7286 7287 7288 7289 7290 7291 7292 7293 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 7316 7317 7318 7319 7320 7321 7322 7323 7324 7325 7326 7327 7328 7329 7330 7331 7332 7333 7334 7335 7336 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 7377 7378 7379 7380 7381 7382 7383 7384 7385 7386 7387 7388 7389 7390 7391 7392 7393 7394 7395 7396 7397 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 7413 7414 7415 7416 7417 7418 7419 7420 7421 7422 7423 7424 7425 7426 7427 7428 7429 7430 7431 7432 7433 7434 7435 7436 7437 7438 7439 7440 7441 7442 7443 7444 7445 7446 7447 7448 7449 7450 7451 7452 7453 7454 7455 7456 7457 7458 7459 7460 7461 7462 7463 7464 7465 7466 7467 7468 7469 7470 7471 7472 7473 7474 7475 7476 7477 7478 7479 7480 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490 7491 7492 7493 7494 7495 7496 7497 7498 7499 7500 7501 7502 7503 7504 7505 7506 7507 7508 7509 7510 7511 7512 7513 7514 7515 7516 7517 7518 7519 7520 7521 7522 7523 7524 7525 7526 7527 7528 7529 7530 7531 7532 7533 7534 7535 7536 7537 7538 7539 7540 7541 7542 7543 7544 7545 7546 7547 7548 7549 7550 7551 7552 7553 7554 7555 7556 7557 7558 7559 7560 7561 7562 7563 7564 7565 7566 7567 7568 7569 7570 7571 7572 7573 7574 7575 7576 7577 7578 7579 7580 7581 7582 7583 7584 7585 7586 7587 7588 7589 7590 7591 7592 7593 7594 7595 7596 7597 7598 7599 7600 7601 7602 7603 7604 7605 7606 7607 7608 7609 7610 7611 7612 7613 7614 7615 7616 7617 7618 7619 7620 7621 7622 7623 7624 7625 7626 7627 7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 7642 7643 7644 7645 7646 7647 7648 7649 7650 7651 7652 7653 7654 7655 7656 7657 7658 7659 7660 7661 7662 7663 7664 7665 7666 7667 7668 7669 7670 7671 7672 7673 7674 7675 7676 7677 7678 7679 7680 7681 7682 7683 7684 7685 7686 7687 7688 7689 7690 7691 7692 7693 7694 7695 7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 7706 7707 7708 7709 7710 7711 7712 7713 7714 7715 7716 7717 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 7734 7735 7736 7737 7738 7739 7740 7741 7742 7743 7744 7745 7746 7747 7748 7749 7750 7751 7752 7753 7754 7755 7756 7757 7758 7759 7760 7761 7762 7763 7764 7765 7766 7767 7768 7769 7770 7771 7772 7773 7774 7775 7776 7777 7778 7779 7780 7781 7782 7783 7784 7785 7786 7787 7788 7789 7790 7791 7792 7793 7794 7795 7796 7797 7798 7799 7800 7801 7802 7803 7804 7805 7806 7807 7808 7809 7810 7811 7812 7813 7814 7815 7816 7817 7818 7819 7820 7821 7822 7823 7824 7825 7826 7827 7828 7829 7830 7831 7832 7833 7834 7835 7836 7837 7838 7839 7840 7841 7842 7843 7844 7845 7846 7847 7848 7849 7850 7851 7852 7853 7854 7855 7856 7857 7858 7859 7860 7861 7862 7863 7864 7865 7866 7867 7868 7869 7870 7871 7872 7873 7874 7875 7876 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 7902 7903 7904 7905 7906 7907 7908 7909 7910 7911 7912 7913 7914 7915 7916 7917 7918 7919 7920 7921 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 7961 7962 7963 7964 7965 7966 7967 7968 7969 7970 7971 7972 7973 7974 7975 7976 7977 7978 7979 7980 7981 7982 7983 7984 7985 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 8001 8002 8003 8004 8005 8006 8007 8008 8009 8010 8011 8012 8013 8014 8015 8016 8017 8018 8019 8020 8021 8022 8023 8024 8025 8026 8027 8028 8029 8030 8031 8032 8033 8034 8035 8036 8037 8038 8039 8040 8041 8042 8043 8044 8045 8046 8047 8048 8049 8050 8051 8052 8053 8054 8055 8056 8057 8058 8059 8060 8061 8062 8063 8064 8065 8066 8067 8068 8069 8070 8071 8072 8073 8074 8075 8076 8077 8078 8079 8080 8081 8082 8083 8084 8085 8086 8087 8088 8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 8126 8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 8198 8199 8200 8201 8202 8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 8213 8214 8215 8216 8217 8218 8219 8220 8221 8222 8223 8224 8225 8226 8227 8228 8229 8230 8231 8232 8233 8234 8235 8236 8237 8238 8239 8240 8241 8242 8243 8244 8245 8246 8247 8248 8249 8250 8251 8252 8253 8254 8255 8256 8257 8258 8259 8260 8261 8262 8263 8264 8265 8266 8267 8268 8269 8270 8271 8272 8273 8274 8275 8276 8277 8278 8279 8280 8281 8282 8283 8284 8285 8286 8287 8288 8289 8290 8291 8292 8293 8294 8295 8296 8297 8298 8299 8300 8301 8302 8303 8304 8305 8306 8307 8308 8309 8310 8311 8312 8313 8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8327 8328 8329 8330 8331 8332 8333 8334 8335 8336 8337 8338 8339 8340 8341 8342 8343 8344 8345 8346 8347 8348 8349 8350 8351 8352 8353 8354 8355 8356 8357 8358 8359 8360 8361 8362 8363 8364 8365 8366 8367 8368 8369 8370 8371 8372 8373 8374 8375 8376 8377 8378 8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 7 7 63 18 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 9 3 1 1 1 1 4 4 502 502 4 1 1 2 1 2 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 12 1 1 1 3 3 3 429 429 429 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 93 93 1 1 1 1 1 1 1 1 1 1 1 1 2 2 166 162 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 169 1 1 6 6 1 1 1 1 1 1 6 1 1 3 3 3 1 5 5 5 5 5 5 595 595 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 1 1 2 2 162 162 162 162 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 164 73 1 1 1 1 1 1 1 1 1 1 172 172 10 1 1 1 1 1 1 1 3 3 1 4 4 1 2 2 1 1 1 1 1 1 | /**
* @license
* Lo-Dash 2.4.1 <http://lodash.com/>
* Copyright 2012-2014 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.6.0 <http://underscorejs.org/LICENSE>
* Copyright 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Available under MIT license <http://lodash.com/license>
*/
;(function() {
/** Used as a safe reference for `undefined` in pre ES5 environments */
var undefined;
/** Used to compose bitmasks for wrapper metadata */
var BIND_FLAG = 1,
BIND_KEY_FLAG = 2,
CURRY_FLAG = 4,
CURRY_BOUND_FLAG = 8,
PARTIAL_FLAG = 16,
PARTIAL_RIGHT_FLAG = 32;
/** Used as the semantic version number */
var version = '2.4.1';
/** Used as the property name for wrapper metadata */
var expando = '__lodash@' + version + '__';
/** Used to generate unique IDs */
var idCounter = 0;
/** Used to detect words composed of all capital letters */
var reAllCaps = /^[A-Z]+$/;
/** Used to match empty string literals in compiled template source */
var reEmptyStringLeading = /\b__p \+= '';/g,
reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
/** Used to match HTML entities and HTML characters */
var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
reUnescapedHtml = /[&<>"']/g;
/** Used to match template delimiters */
var reEscape = /<%-([\s\S]+?)%>/g,
reEvaluate = /<%([\s\S]+?)%>/g,
reInterpolate = /<%=([\s\S]+?)%>/g;
/**
* Used to match ES6 template delimiters.
* See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components)
* for more details.
*/
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
/** Used to match `RegExp` flags from their coerced string values */
var reFlags = /\w*$/;
/** Used to detected named functions */
var reFuncName = /^\s*function[ \n\r\t]+\w/;
/** Used to detect hexadecimal string values */
var reHexPrefix = /^0[xX]/;
/** Used to match latin-1 supplement letters */
var reLatin1 = /[\xC0-\xFF]/g;
/** Used to ensure capturing order of template delimiters */
var reNoMatch = /($^)/;
/**
* Used to match `RegExp` special characters.
* See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
* for more details.
*/
var reRegExpChars = /[.*+?^${}()|[\]\\]/g;
/** Used to detect functions containing a `this` reference */
var reThis = /\bthis\b/;
/** Used to match unescaped characters in compiled string literals */
var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
/** Used to match words to create compound words */
var reWords = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[a-z]+|[0-9]+/g;
/** Used to detect and test whitespace */
var whitespace = (
// whitespace
' \t\x0B\f\xA0\ufeff' +
// line terminators
'\n\r\u2028\u2029' +
// unicode category "Zs" space separators
'\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
);
/** Used to assign default `context` object properties */
var contextProps = [
'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object',
'RegExp', 'Set', 'String', '_', 'clearTimeout', 'document', 'isFinite', 'isNaN',
'parseInt', 'setTimeout', 'TypeError', 'window', 'WinRTError'
];
/** Used to fix the JScript `[[DontEnum]]` bug */
var shadowedProps = [
'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
'toLocaleString', 'toString', 'valueOf'
];
/** Used to make template `sourceURL`s easier to identify */
var templateCounter = 0;
/** `Object#toString` result shortcuts */
var argsClass = '[object Arguments]',
arrayClass = '[object Array]',
boolClass = '[object Boolean]',
dateClass = '[object Date]',
errorClass = '[object Error]',
funcClass = '[object Function]',
numberClass = '[object Number]',
objectClass = '[object Object]',
regexpClass = '[object RegExp]',
stringClass = '[object String]';
/** Used to identify object classifications that `_.clone` supports */
var cloneableClasses = {};
cloneableClasses[funcClass] = false;
cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
cloneableClasses[boolClass] = cloneableClasses[dateClass] =
cloneableClasses[numberClass] = cloneableClasses[objectClass] =
cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
/** Used as an internal `_.debounce` options object by `_.throttle` */
var debounceOptions = {
'leading': false,
'maxWait': 0,
'trailing': false
};
/** Used as the property descriptor for wrapper metadata */
var descriptor = {
'configurable': false,
'enumerable': false,
'value': null,
'writable': false
};
/**
* Used to convert characters to HTML entities.
*
* Note: Though the ">" character is escaped for symmetry, characters like
* ">", "`", and "/" don't require escaping in HTML and have no special meaning
* unless they're part of a tag or unquoted attribute value.
* See [Mathias' article](http://mathiasbynens.be/notes/ambiguous-ampersands)
* (under "semi-related fun fact") for more details.
*/
var htmlEscapes = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
/** Used to convert HTML entities to characters */
var htmlUnescapes = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
''': "'"
};
/**
* Used to convert latin-1 supplement letters to basic latin (ASCII) letters.
* See [Wikipedia](http://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
* for more details.
*/
var deburredLetters = {
'\xC0': 'A', '\xC1': 'A', '\xC2': 'A', '\xC3': 'A', '\xC4': 'A', '\xC5': 'A',
'\xE0': 'a', '\xE1': 'a', '\xE2': 'a', '\xE3': 'a', '\xE4': 'a', '\xE5': 'a',
'\xC7': 'C', '\xE7': 'c',
'\xD0': 'D', '\xF0': 'd',
'\xC8': 'E', '\xC9': 'E', '\xCA': 'E', '\xCB': 'E',
'\xE8': 'e', '\xE9': 'e', '\xEA': 'e', '\xEB': 'e',
'\xCC': 'I', '\xCD': 'I', '\xCE': 'I', '\xCF': 'I',
'\xEC': 'i', '\xED': 'i', '\xEE': 'i', '\xEF': 'i',
'\xD1': 'N', '\xF1': 'n',
'\xD2': 'O', '\xD3': 'O', '\xD4': 'O', '\xD5': 'O', '\xD6': 'O', '\xD8': 'O',
'\xF2': 'o', '\xF3': 'o', '\xF4': 'o', '\xF5': 'o', '\xF6': 'o', '\xF8': 'o',
'\xD9': 'U', '\xDA': 'U', '\xDB': 'U', '\xDC': 'U',
'\xF9': 'u', '\xFA': 'u', '\xFB': 'u', '\xFC': 'u',
'\xDD': 'Y', '\xFD': 'y', '\xFF': 'y',
'\xC6': 'AE', '\xE6': 'ae',
'\xDE': 'Th', '\xFE': 'th',
'\xDF': 'ss', '\xD7': ' ', '\xF7': ' '
};
/** Used to determine if values are of the language type `Object` */
var objectTypes = {
'function': true,
'object': true
};
/** Used to escape characters for inclusion in compiled string literals */
var stringEscapes = {
'\\': '\\',
"'": "'",
'\n': 'n',
'\r': 'r',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
/** Used as a reference to the global object */
var root = (objectTypes[typeof window] && window) || this;
/** Detect free variable `exports` */
var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
/** Detect free variable `module` */
var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
/** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;
Eif (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {
root = freeGlobal;
}
/** Detect the popular CommonJS extension `module.exports` */
var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
/*--------------------------------------------------------------------------*/
/**
* Used by `_.defaults` to customize its `_.assign` use.
*
* @private
* @param {*} objectValue The destination object property value.
* @param {*} sourceValue The source object property value.
* @returns {*} Returns the value to assign to the destination object.
*/
function assignDefaults(objectValue, sourceValue) {
return typeof objectValue == 'undefined' ? sourceValue : objectValue;
}
/**
* The base implementation of `compareAscending` used to compare values and
* sort them in ascending order without guaranteeing a stable sort.
*
* @private
* @param {*} value The value to compare to `other`.
* @param {*} other The value to compare to `value`.
* @returns {number} Returns the sort order indicator for `value`.
*/
function baseCompareAscending(value, other) {
if (value !== other) {
if (value > other || typeof value == 'undefined') {
return 1;
}
if (value < other || typeof other == 'undefined') {
return -1;
}
}
return 0;
}
/**
* The base implementation of `_.indexOf` without support for binary searches.
*
* @private
* @param {Array} array The array to search.
* @param {*} value The value to search for.
* @param {number} [fromIndex=0] The index to search from.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseIndexOf(array, value, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array ? array.length : 0;
while (++index < length) {
if (array[index] === value) {
return index;
}
}
return -1;
}
/**
* An implementation of `_.contains` for cache objects that mimics the return
* signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
*
* @private
* @param {Object} cache The cache object to inspect.
* @param {*} value The value to search for.
* @returns {number} Returns `0` if `value` is found, else `-1`.
*/
function cacheIndexOf(cache, value) {
return cache.has(value) ? 0 : -1;
}
/**
* Used by `_.max` and `_.min` as the default callback when a given
* collection is a string value.
*
* @private
* @param {string} string The string to inspect.
* @returns {number} Returns the code unit of the first character of the string.
*/
function charAtCallback(string) {
return string.charCodeAt(0);
}
/**
* Gets the index of the first character of `string` that is not found in `chars`.
*
* @private
* @param {string} string The string to inspect.
* @param {string} chars The characters to find.
* @returns {number} Returns the index of the first character not found in `chars`.
*/
function charsLeftIndex(string, chars) {
var index = -1,
length = string.length;
while (++index < length) {
if (chars.indexOf(string.charAt(index)) < 0) {
break;
}
}
return index;
}
/**
* Gets the index of the last character of `string` that is not found in `chars`.
*
* @private
* @param {string} string The string to inspect.
* @param {string} chars The characters to find.
* @returns {number} Returns the index of the last character not found in `chars`.
*/
function charsRightIndex(string, chars) {
var index = string.length;
while (index--) {
if (chars.indexOf(string.charAt(index)) < 0) {
break;
}
}
return index;
}
/**
* Used by `_.sortBy` to compare transformed elements of a collection and stable
* sort them in ascending order.
*
* @private
* @param {Object} object The object to compare to `other`.
* @param {Object} other The object to compare to `object`.
* @returns {number} Returns the sort order indicator for `object`.
*/
function compareAscending(object, other) {
return baseCompareAscending(object.criteria, other.criteria) || object.index - other.index;
}
/**
* Used by `_.sortBy` to compare multiple properties of each element in a
* collection and stable sort them in ascending order.
*
* @private
* @param {Object} object The object to compare to `other`.
* @param {Object} other The object to compare to `object`.
* @returns {number} Returns the sort order indicator for `object`.
*/
function compareMultipleAscending(object, other) {
var index = -1,
objCriteria = object.criteria,
othCriteria = other.criteria,
length = objCriteria.length;
while (++index < length) {
var result = baseCompareAscending(objCriteria[index], othCriteria[index]);
if (result) {
return result;
}
}
// Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
// that causes it, under certain circumstances, to provide the same value
// for `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
//
// This also ensures a stable sort in V8 and other engines.
// See https://code.google.com/p/v8/issues/detail?id=90
return object.index - other.index;
}
/**
* Creates a function that produces compound words out of the words in a
* given string.
*
* @private
* @param {Function} callback The function called to combine each word.
* @returns {Function} Returns the new compounder function.
*/
function createCompounder(callback) {
return function(string) {
var index = -1,
words = string != null && String(string).replace(reLatin1, deburrLetter).match(reWords),
length = words ? words.length : 0,
result = '';
while (++index < length) {
result = callback(result, words[index], index, words);
}
return result;
};
}
/**
* Used by `createCompounder` to convert latin-1 supplement letters to basic
* latin (ASCII) letters.
*
* @private
* @param {string} letter The matched letter to deburr.
* @returns {string} Returns the deburred letter.
*/
function deburrLetter(letter) {
return deburredLetters[letter];
}
/**
* Used by `_.escape` to convert characters to HTML entities.
*
* @private
* @param {string} chr The matched character to escape.
* @returns {string} Returns the escaped character.
*/
function escapeHtmlChar(chr) {
return htmlEscapes[chr];
}
/**
* Used by `_.template` to escape characters for inclusion in compiled
* string literals.
*
* @private
* @param {string} chr The matched character to escape.
* @returns {string} Returns the escaped character.
*/
function escapeStringChar(chr) {
return '\\' + stringEscapes[chr];
}
/**
* Checks if `value` is a DOM node in IE < 9.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a DOM node, else `false`.
*/
function isNode(value) {
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
// methods that are `typeof` "string" and still can coerce nodes to strings
return typeof value.toString != 'function' && typeof (value + '') == 'string';
}
/**
* A fallback implementation of `String#trim` to remove leading and trailing
* whitespace or specified characters from `string`.
*
* @private
* @param {string} string The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
*/
function shimTrim(string, chars) {
string = string == null ? '' : String(string);
if (!string) {
return string;
}
if (chars == null) {
return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1);
}
chars = String(chars);
return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1);
}
/**
* A fallback implementation of `String#trimLeft` to remove leading whitespace or
* specified characters from `string`.
*
* @private
* @param {string} string The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
*/
function shimTrimLeft(string, chars) {
string = string == null ? '' : String(string);
if (!string) {
return string;
}
if (chars == null) {
return string.slice(trimmedLeftIndex(string))
}
chars = String(chars);
return string.slice(charsLeftIndex(string, chars));
}
/**
* A fallback implementation of `String#trimRight` to remove trailing whitespace or
* specified characters from `string`.
*
* @private
* @param {string} string The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
*/
function shimTrimRight(string, chars) {
string = string == null ? '' : String(string);
if (!string) {
return string;
}
if (chars == null) {
return string.slice(0, trimmedRightIndex(string) + 1)
}
chars = String(chars);
return string.slice(0, charsRightIndex(string, chars) + 1);
}
/**
* Gets the index of the first non-whitespace character of `string`.
*
* @private
* @param {string} string The string to inspect.
* @returns {number} Returns the index of the first non-whitespace character.
*/
function trimmedLeftIndex(string) {
var index = -1,
length = string.length;
while (++index < length) {
var c = string.charCodeAt(index);
if (!((c <= 160 && (c >= 9 && c <= 13) || c == 32 || c == 160) || c == 5760 || c == 6158 ||
(c >= 8192 && (c <= 8202 || c == 8232 || c == 8233 || c == 8239 || c == 8287 || c == 12288 || c == 65279)))) {
break;
}
}
return index;
}
/**
* Gets the index of the last non-whitespace character of `string`.
*
* @private
* @param {string} string The string to inspect.
* @returns {number} Returns the index of the last non-whitespace character.
*/
function trimmedRightIndex(string) {
var index = string.length;
while (index--) {
var c = string.charCodeAt(index);
if (!((c <= 160 && (c >= 9 && c <= 13) || c == 32 || c == 160) || c == 5760 || c == 6158 ||
(c >= 8192 && (c <= 8202 || c == 8232 || c == 8233 || c == 8239 || c == 8287 || c == 12288 || c == 65279)))) {
break;
}
}
return index;
}
/**
* Used by `_.unescape` to convert HTML entities to characters.
*
* @private
* @param {string} chr The matched character to unescape.
* @returns {string} Returns the unescaped character.
*/
function unescapeHtmlChar(chr) {
return htmlUnescapes[chr];
}
/*--------------------------------------------------------------------------*/
/**
* Create a new `lodash` function using the given `context` object.
*
* @static
* @memberOf _
* @category Utilities
* @param {Object} [context=root] The context object.
* @returns {Function} Returns a new `lodash` function.
*/
function runInContext(context) {
// Avoid issues with some ES3 environments that attempt to use values, named
// after built-in constructors like `Object`, for the creation of literals.
// ES5 clears this up by stating that literals must use built-in constructors.
// See http://es5.github.io/#x11.1.5.
context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
/** Native constructor references */
var Array = context.Array,
Boolean = context.Boolean,
Date = context.Date,
Error = context.Error,
Function = context.Function,
Math = context.Math,
Number = context.Number,
Object = context.Object,
RegExp = context.RegExp,
String = context.String,
TypeError = context.TypeError;
/** Used for native method references */
var arrayRef = Array.prototype,
errorProto = Error.prototype,
objectProto = Object.prototype,
stringProto = String.prototype;
/** Used to detect DOM support */
var document = (document = context.window) && document.document;
/** Used to restore the original `_` reference in `_.noConflict` */
var oldDash = context._;
/**
* Used as the maximum length of an array-like object.
* See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
* for more details.
*/
var maxSafeInteger = Math.pow(2, 53) - 1;
/** Used to resolve the internal `[[Class]]` of values */
var toString = objectProto.toString;
/** Used to detect if a method is native */
var reNative = RegExp('^' +
escapeRegExp(toString)
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);
/** Native method shortcuts */
var ceil = Math.ceil,
clearTimeout = context.clearTimeout,
floor = Math.floor,
fnToString = Function.prototype.toString,
getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
hasOwnProperty = objectProto.hasOwnProperty,
push = arrayRef.push,
propertyIsEnumerable = objectProto.propertyIsEnumerable,
Set = isNative(Set = context.Set) && Set,
setTimeout = context.setTimeout,
splice = arrayRef.splice,
unshift = arrayRef.unshift;
/** Used to set metadata on functions */
var defineProperty = (function() {
// IE 8 only accepts DOM elements
try {
var o = {},
func = isNative(func = Object.defineProperty) && func,
result = func(o, o, o) && func;
} catch(e) { }
return result;
}());
/* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeContains = isNative(nativeContains = stringProto.contains) && nativeContains,
nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = context.isFinite,
nativeIsNaN = context.isNaN,
nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
nativeMax = Math.max,
nativeMin = Math.min,
nativeNow = isNative(nativeNow = Date.now) && nativeNow,
nativeParseInt = context.parseInt,
nativeRandom = Math.random,
nativeTrim = isNative(nativeTrim = stringProto.trim) && !nativeTrim.call(whitespace) && nativeTrim,
nativeTrimLeft = isNative(nativeTrimLeft = stringProto.trimLeft) && !nativeTrimLeft.call(whitespace) && nativeTrimLeft,
nativeTrimRight = isNative(nativeTrimRight = stringProto.trimRight) && !nativeTrimRight.call(whitespace) && nativeTrimRight;
/** Used to lookup a built-in constructor by `[[Class]]` */
var ctorByClass = {};
ctorByClass[arrayClass] = Array;
ctorByClass[boolClass] = Boolean;
ctorByClass[dateClass] = Date;
ctorByClass[funcClass] = Function;
ctorByClass[objectClass] = Object;
ctorByClass[numberClass] = Number;
ctorByClass[regexpClass] = RegExp;
ctorByClass[stringClass] = String;
/** Used to avoid iterating over non-enumerable properties in IE < 9 */
var nonEnumProps = {};
nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true };
nonEnumProps[objectClass] = { 'constructor': true };
(function() {
var length = shadowedProps.length;
while (length--) {
var key = shadowedProps[length];
for (var className in nonEnumProps) {
if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) {
nonEnumProps[className][key] = false;
}
}
}
}());
/*--------------------------------------------------------------------------*/
/**
* Creates a `lodash` object which wraps the given value to enable intuitive
* method chaining.
*
* In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
* `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
* and `unshift`
*
* Chaining is supported in custom builds as long as the `value` method is
* implicitly or explicitly included in the build.
*
* The chainable wrapper functions are:
* `after`, `assign`, `at`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
* `compose`, `concat`, `constant`, `countBy`, `create`, `createCallback`,
* `curry`, `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`,
* `flatten`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
* `forOwnRight`, `functions`, `groupBy`, `indexBy`, `initial`, `intersection`,
* `invert`, `invoke`, `keys`, `map`, `mapValues`, `matches`, `max`, `memoize`,
* `merge`, `min`, `noop`, `object`, `omit`, `once`, `pairs`, `partial`,
* `partialRight`, `pick`, `pluck`, `property`, `pull`, `push`, `range`,
* `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`,
* `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, `union`,
* `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, `xor`,
* and `zip`
*
* The non-chainable wrapper functions are:
* `capitalize`, `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`,
* `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`,
* `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`,
* `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`,
* `isNull`, `isNumber`, `isObject`, `isPlainObject`, `isRegExp`, `isString`,
* `isUndefined`, `join`, `lastIndexOf`, `mixin`, `noConflict`, `now`,
* `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`,
* `size`, `some`, `sortedIndex`, `runInContext`, `template`, `trim`,
* `trimLeft`, `trimRight`, `unescape`, `uniqueId`, and `value`
*
* The wrapper functions `first`, `last`, and `sample` return wrapped values
* when `n` is provided, otherwise they return unwrapped values.
*
* Explicit chaining can be enabled by using the `_.chain` method.
*
* @name _
* @constructor
* @category Chaining
* @param {*} value The value to wrap in a `lodash` instance.
* @returns {Object} Returns a `lodash` instance.
* @example
*
* var wrapped = _([1, 2, 3]);
*
* // returns an unwrapped value
* wrapped.reduce(function(sum, num) {
* return sum + num;
* });
* // => 6
*
* // returns a wrapped value
* var squares = wrapped.map(function(num) {
* return num * num;
* });
*
* _.isArray(squares);
* // => false
*
* _.isArray(squares.value());
* // => true
*/
function lodash(value) {
// don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
? value
: new lodashWrapper(value);
}
/**
* A fast path for creating `lodash` wrapper objects.
*
* @private
* @param {*} value The value to wrap in a `lodash` instance.
* @param {boolean} [chainAll=false] A flag to enable chaining for all methods.
* @returns {Object} Returns a `lodash` instance.
*/
function lodashWrapper(value, chainAll) {
this.__chain__ = !!chainAll;
this.__wrapped__ = value;
}
// ensure `new lodashWrapper` is an instance of `lodash`
lodashWrapper.prototype = lodash.prototype;
/**
* An object used to flag environments features.
*
* @static
* @memberOf _
* @type Object
*/
var support = lodash.support = {};
(function(x) {
var ctor = function() { this.x = 1; },
object = { '0': 1, 'length': 1 },
props = [];
ctor.prototype = { 'valueOf': 1, 'y': 1 };
for (var key in new ctor) { props.push(key); }
for (var argsKey in arguments) { }
for (var strKey in 'x') { }
/**
* Detect if an `arguments` object's `[[Class]]` is resolvable
* (all but Firefox < 4, IE < 9).
*
* @memberOf _.support
* @type boolean
*/
support.argsClass = toString.call(arguments) == argsClass;
/**
* Detect if `arguments` objects are `Object` objects
* (all but Narwhal and Opera < 10.5).
*
* @memberOf _.support
* @type boolean
*/
support.argsObject = arguments.constructor == Object && !(arguments instanceof Array);
/**
* Detect if `name` or `message` properties of `Error.prototype` are
* enumerable by default (IE < 9, Safari < 5.1).
*
* @memberOf _.support
* @type boolean
*/
support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
propertyIsEnumerable.call(errorProto, 'name');
/**
* Detect if `prototype` properties are enumerable by default.
*
* Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
* (if the prototype or a property on the prototype has been set)
* incorrectly sets the `[[Enumerable]]` value of a function's `prototype`
* property to `true`.
*
* @memberOf _.support
* @type boolean
*/
support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype');
/**
* Detect if functions can be decompiled by `Function#toString`
* (all but PS3 and older Opera mobile browsers; forced `false` for Windows 8 apps).
*
* @memberOf _.support
* @type boolean
*/
support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
/**
* Detect if `Function#name` is supported (all but IE).
*
* @memberOf _.support
* @type boolean
*/
support.funcNames = typeof Function.name == 'string';
/**
* Detect if string indexes are non-enumerable
* (IE < 9, RingoJS, Rhino, Narwhal).
*
* @memberOf _.support
* @type boolean
*/
support.nonEnumStrings = strKey != '0';
/**
* Detect if properties shadowing those on `Object.prototype` are
* non-enumerable.
*
* In IE < 9 an object's own properties, shadowing non-enumerable ones,
* are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
*
* @memberOf _.support
* @type boolean
*/
support.nonEnumShadows = !/valueOf/.test(props);
/**
* Detect if own properties are iterated after inherited properties
* (all but IE < 9).
*
* @memberOf _.support
* @type boolean
*/
support.ownLast = props[0] != 'x';
/**
* Detect if `Array#shift` and `Array#splice` augment array-like objects
* correctly.
*
* Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
* and `splice()` functions that fail to remove the last element, `value[0]`,
* of array-like objects even though the `length` property is set to `0`.
* The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
* is buggy regardless of mode in IE < 9 and buggy in compatibility mode
* in IE 9.
*
* @memberOf _.support
* @type boolean
*/
support.spliceObjects = (splice.call(object, 0, 1), !object[0]);
/**
* Detect lack of support for accessing string characters by index.
*
* IE < 8 can't access characters by index. IE 8 can only access characters
* by index on string literals, not string objects.
*
* @memberOf _.support
* @type boolean
*/
support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
/**
* Detect if the DOM is supported.
*
* @memberOf _.support
* @type boolean
*/
try {
support.dom = false;
// support.dom = document.createDocumentFragment().nodeType === 11;
} catch(e) {
support.dom = false;
}
/**
* Detect if a DOM node's `[[Class]]` is resolvable (all but IE < 9)
* and that the JS engine errors when attempting to coerce an object to
* a string without a `toString` function.
*
* @memberOf _.support
* @type boolean
*/
try {
support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + ''));
} catch(e) {
support.nodeClass = true;
}
/**
* Detect if `arguments` object indexes are non-enumerable.
*
* In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object
* indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat
* `arguments` object indexes as non-enumerable and fail `hasOwnProperty`
* checks for indexes that exceed their function's formal parameters with
* associated values of `0`.
*
* @memberOf _.support
* @type boolean
*/
try {
support.nonEnumArgs = !(argsKey == '1' && hasOwnProperty.call(arguments, argsKey) &&
propertyIsEnumerable.call(arguments, argsKey));
} catch(e) {
support.nonEnumArgs = true;
}
}(0, 0));
/**
* By default, the template delimiters used by Lo-Dash are similar to those
* in embedded Ruby (ERB). Change the following template settings to use
* alternative delimiters.
*
* @static
* @memberOf _
* @type Object
*/
lodash.templateSettings = {
/**
* Used to detect `data` property values to be HTML-escaped.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'escape': reEscape,
/**
* Used to detect code to be evaluated.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'evaluate': reEvaluate,
/**
* Used to detect `data` property values to inject.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'interpolate': reInterpolate,
/**
* Used to reference the data object in the template text.
*
* @memberOf _.templateSettings
* @type string
*/
'variable': '',
/**
* Used to import variables into the compiled template.
*
* @memberOf _.templateSettings
* @type Object
*/
'imports': {
/**
* A reference to the `lodash` function.
*
* @memberOf _.templateSettings.imports
* @type Function
*/
'_': lodash
}
};
/*--------------------------------------------------------------------------*/
/**
* The base implementation of `_.bind` that creates the bound function and
* sets its metadata.
*
* @private
* @param {Array} data The metadata array.
* @returns {Function} Returns the new bound function.
*/
function baseBind(data) {
var func = data[0],
thisArg = data[3],
partialArgs = data[4],
partialHolders = data[6];
function bound() {
// `Function#bind` spec
// http://es5.github.io/#x15.3.4.5
if (partialArgs) {
// avoid `arguments` object use disqualifying optimizations by
// converting it to an array before passing it to `composeArgs`
var index = -1,
length = arguments.length,
args = Array(length);
while (++index < length) {
args[index] = arguments[index];
}
args = composeArgs(partialArgs, partialHolders, args);
}
// mimic the constructor's `return` behavior
// http://es5.github.io/#x13.2.2
if (this instanceof bound) {
// ensure `new bound` is an instance of `func`
var thisBinding = baseCreate(func.prototype),
result = func.apply(thisBinding, args || arguments);
return isObject(result) ? result : thisBinding;
}
return func.apply(thisArg, args || arguments);
}
setData(bound, data);
return bound;
}
/**
* The base implementation of `_.clone` without argument juggling or support
* for `this` binding.
*
* @private
* @param {*} value The value to clone.
* @param {boolean} [isDeep=false] Specify a deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {Array} [stackA=[]] Tracks traversed source objects.
* @param {Array} [stackB=[]] Associates clones with source counterparts.
* @returns {*} Returns the cloned value.
*/
function baseClone(value, isDeep, callback, stackA, stackB) {
if (callback) {
var result = callback(value);
if (typeof result != 'undefined') {
return result;
}
}
// inspect `[[Class]]`
var isObj = isObject(value);
if (isObj) {
var className = toString.call(value);
if (!cloneableClasses[className] || (!support.nodeClass && isNode(value))) {
return value;
}
var ctor = ctorByClass[className];
switch (className) {
case boolClass:
case dateClass:
return new ctor(+value);
case numberClass:
case stringClass:
return new ctor(value);
case regexpClass:
result = ctor(value.source, reFlags.exec(value));
result.lastIndex = value.lastIndex;
return result;
}
} else {
return value;
}
var isArr = isArray(value);
if (isDeep) {
// check for circular references and return corresponding clone
stackA || (stackA = []);
stackB || (stackB = []);
var length = stackA.length;
while (length--) {
if (stackA[length] == value) {
return stackB[length];
}
}
result = isArr ? ctor(value.length) : {};
}
else {
result = isArr ? slice(value) : assign({}, value);
}
// add array properties assigned by `RegExp#exec`
if (isArr) {
if (hasOwnProperty.call(value, 'index')) {
result.index = value.index;
}
if (hasOwnProperty.call(value, 'input')) {
result.input = value.input;
}
}
// exit for shallow clone
if (!isDeep) {
return result;
}
// add the source value to the stack of traversed objects
// and associate it with its clone
stackA.push(value);
stackB.push(result);
// recursively populate clone (susceptible to call stack limits)
(isArr ? baseEach : baseForOwn)(value, function(valValue, key) {
result[key] = baseClone(valValue, isDeep, callback, stackA, stackB);
});
return result;
}
/**
* The base implementation of `_.create` without support for assigning
* properties to the created object.
*
* @private
* @param {Object} prototype The object to inherit from.
* @returns {Object} Returns the new object.
*/
function baseCreate(prototype) {
return isObject(prototype) ? nativeCreate(prototype) : {};
}
// fallback for environments without `Object.create`
Iif (!nativeCreate) {
baseCreate = (function() {
function Object() {}
return function(prototype) {
if (isObject(prototype)) {
Object.prototype = prototype;
var result = new Object;
Object.prototype = null;
}
return result || context.Object();
};
}());
}
/**
* The base implementation of `_.createCallback` without support for creating
* "_.pluck" or "_.where" style callbacks.
*
* @private
* @param {*} [func=identity] The value to convert to a callback.
* @param {*} [thisArg] The `this` binding of the created callback.
* @param {number} [argCount] The number of arguments the callback accepts.
* @returns {Function} Returns the new function.
*/
function baseCreateCallback(func, thisArg, argCount) {
if (typeof func != 'function') {
return identity;
}
// exit early for no `thisArg` or already bound by `Function#bind`
if (typeof thisArg == 'undefined' || !('prototype' in func)) {
return func;
}
var data = func[expando];
if (typeof data == 'undefined') {
if (support.funcNames) {
data = !func.name;
}
data = data || !support.funcDecomp;
if (!data) {
var source = fnToString.call(func);
if (!support.funcNames) {
data = !reFuncName.test(source);
}
if (!data) {
// checks if `func` references the `this` keyword and stores the result
data = reThis.test(source);
setData(func, data);
}
}
}
// exit early if there are no `this` references or `func` is bound
if (data === false || (data !== true && data[1] & BIND_FLAG)) {
return func;
}
switch (argCount) {
case 1: return function(value) {
return func.call(thisArg, value);
};
case 2: return function(value, other) {
return func.call(thisArg, value, other);
};
case 3: return function(value, index, collection) {
return func.call(thisArg, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(thisArg, accumulator, value, index, collection);
};
}
return bind(func, thisArg);
}
/**
* The base implementation of `createWrapper` that creates the wrapper and
* sets its metadata.
*
* @private
* @param {Array} data The metadata array.
* @returns {Function} Returns the new function.
*/
function baseCreateWrapper(data) {
var func = data[0],
bitmask = data[1],
arity = data[2],
thisArg = data[3],
partialArgs = data[4],
partialRightArgs = data[5],
partialHolders = data[6],
partialRightHolders = data[7];
var isBind = bitmask & BIND_FLAG,
isBindKey = bitmask & BIND_KEY_FLAG,
isCurry = bitmask & CURRY_FLAG,
isCurryBound = bitmask & CURRY_BOUND_FLAG,
key = func;
function bound() {
var index = -1,
length = arguments.length,
args = Array(length);
while (++index < length) {
args[index] = arguments[index];
}
if (partialArgs) {
args = composeArgs(partialArgs, partialHolders, args);
}
if (partialRightArgs) {
args = composeArgsRight(partialRightArgs, partialRightHolders, args);
}
if (isCurry) {
var newPartialHolders = getHolders(args);
length -= newPartialHolders.length;
if (length < arity) {
bitmask |= PARTIAL_FLAG;
bitmask &= ~PARTIAL_RIGHT_FLAG
if (!isCurryBound) {
bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
}
var newArity = nativeMax(arity - length, 0);
return baseCreateWrapper([func, bitmask, newArity, thisArg, args, null, newPartialHolders]);
}
}
var thisBinding = isBind ? thisArg : this;
if (isBindKey) {
func = thisBinding[key];
}
if (this instanceof bound) {
thisBinding = baseCreate(func.prototype);
var result = func.apply(thisBinding, args);
return isObject(result) ? result : thisBinding;
}
return func.apply(thisBinding, args);
}
setData(bound, data);
return bound;
}
/**
* The base implementation of `_.difference` that accepts a single array
* of values to exclude.
*
* @private
* @param {Array} array The array to process.
* @param {Array} [values] The array of values to exclude.
* @returns {Array} Returns the new array of filtered values.
*/
function baseDifference(array, values) {
var length = array ? array.length : 0;
if (!length) {
return [];
}
var index = -1,
indexOf = getIndexOf(),
prereq = indexOf === baseIndexOf,
isLarge = prereq && createCache && values && values.length >= 200,
isCommon = prereq && !isLarge,
result = [],
valuesLength = values ? values.length : 0;
if (isLarge) {
indexOf = cacheIndexOf;
values = createCache(values);
}
outer:
while (++index < length) {
var value = array[index];
if (isCommon) {
var valuesIndex = valuesLength;
while (valuesIndex--) {
if (values[valuesIndex] === value) {
continue outer;
}
}
result.push(value);
}
else if (indexOf(values, value) < 0) {
result.push(value);
}
}
return result;
}
/**
* The base implementation of `_.forEach` without support for callback
* shorthands or `this` binding.
*
* @private
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} callback The function called per iteration.
* @returns {Array|Object|string} Returns `collection`.
*/
function baseEach(collection, callback) {
var index = -1,
iterable = collection,
length = collection ? collection.length : 0;
Eif (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
Iif (support.unindexedChars && isString(iterable)) {
iterable = iterable.split('');
}
while (++index < length) {
Iif (callback(iterable[index], index, collection) === false) {
break;
}
}
} else {
baseForOwn(collection, callback);
}
return collection;
}
/**
* The base implementation of `_.forEachRight` without support for callback
* shorthands or `this` binding.
*
* @private
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} callback The function called per iteration.
* @returns {Array|Object|string} Returns `collection`.
*/
function baseEachRight(collection, callback) {
var iterable = collection,
length = collection ? collection.length : 0;
if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
if (support.unindexedChars && isString(iterable)) {
iterable = iterable.split('');
}
while (length--) {
if (callback(iterable[length], length, collection) === false) {
break;
}
}
} else {
baseForOwnRight(collection, callback);
}
return collection;
}
/**
* The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`
* without support for callback shorthands or `this` binding which iterates
* over `collection` using the provided `eachFunc`.
*
* @private
* @param {Array|Object|string} collection The collection to search.
* @param {Function} predicate The function called per iteration.
* @param {Function} eachFunc The function to iterate over the collection.
* @param {boolean} [retKey=false] A flag to indicate returning the key of
* the found element instead of the element itself.
* @returns {*} Returns the found element or its key, else `undefined`.
*/
function baseFind(collection, predicate, eachFunc, retKey) {
var result;
eachFunc(collection, function(value, key, collection) {
if (predicate(value, key, collection)) {
result = retKey ? key : value;
return false;
}
});
return result;
}
/**
* The base implementation of `_.flatten` without support for callback
* shorthands or `this` binding.
*
* @private
* @param {Array} array The array to flatten.
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
* @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
* @param {number} [fromIndex=0] The index to start from.
* @returns {Array} Returns the new flattened array.
*/
function baseFlatten(array, isShallow, isStrict, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array ? array.length : 0,
result = [];
while (++index < length) {
var value = array[index];
if (value && typeof value == 'object' && typeof value.length == 'number'
&& (isArray(value) || isArguments(value))) {
// recursively flatten arrays (susceptible to call stack limits)
if (!isShallow) {
value = baseFlatten(value, isShallow, isStrict);
}
var valIndex = -1,
valLength = value.length,
resIndex = result.length;
result.length += valLength;
while (++valIndex < valLength) {
result[resIndex++] = value[valIndex];
}
} else if (!isStrict) {
result.push(value);
}
}
return result;
}
/**
* The base implementation of `baseForIn` and `baseForOwn` which iterates
* over `object` properties returned by `keysFunc` executing the callback
* for each property. Callbacks may exit iteration early by explicitly
* returning `false`.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
function baseFor(object, callback, keysFunc) {
var index = -1,
props = keysFunc(object),
length = props.length;
while (++index < length) {
var key = props[index];
Iif (callback(object[key], key, object) === false) {
break;
}
}
return object;
}
/**
* This function is like `baseFor` except that it iterates over properties
* in the opposite order.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
function baseForRight(object, callback, keysFunc) {
var props = keysFunc(object),
length = props.length;
while (length--) {
var key = props[length];
if (callback(object[key], key, object) === false) {
break;
}
}
return object;
}
/**
* The base implementation of `_.forIn` without support for callback
* shorthands or `this` binding.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @returns {Object} Returns `object`.
*/
function baseForIn(object, callback) {
return baseFor(object, callback, keysIn);
}
/**
* The base implementation of `_.forOwn` without support for callback
* shorthands or `this` binding.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @returns {Object} Returns `object`.
*/
function baseForOwn(object, callback) {
return baseFor(object, callback, keys);
}
/**
* The base implementation of `_.forOwnRight` without support for callback
* shorthands or `this` binding.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
* @returns {Object} Returns `object`.
*/
function baseForOwnRight(object, callback) {
return baseForRight(object, callback, keys);
}
/**
* The base implementation of `_.isEqual`, without support for `thisArg`
* binding, that allows partial "_.where" style comparisons.
*
* @private
* @param {*} value The value to compare to `other`.
* @param {*} other The value to compare to `value`.
* @param {Function} [callback] The function to customize comparing values.
* @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
* @param {Array} [stackA=[]] Tracks traversed `value` objects.
* @param {Array} [stackB=[]] Tracks traversed `other` objects.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
*/
function baseIsEqual(value, other, callback, isWhere, stackA, stackB) {
if (callback) {
var result = callback(value, other);
if (typeof result != 'undefined') {
return !!result;
}
}
// exit early for identical values
if (value === other) {
// treat `+0` vs. `-0` as not equal
return value !== 0 || (1 / value == 1 / other);
}
var valType = typeof value,
othType = typeof other;
// exit early for unlike primitive values
if (value === value && (value == null || other == null ||
(valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) {
return false;
}
// compare `[[Class]]` names
var valClass = toString.call(value),
othClass = toString.call(other),
valIsArg = valClass == argsClass,
othIsArg = othClass == argsClass;
if (valIsArg) {
valClass = objectClass;
}
if (othIsArg) {
othClass = objectClass;
}
if (valClass != othClass) {
return false;
}
switch (valClass) {
case boolClass:
case dateClass:
// coerce dates and booleans to numbers, dates to milliseconds and booleans
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal
return +value == +other;
case numberClass:
// treat `NaN` vs. `NaN` as equal
return (value != +value)
? other != +other
// but treat `-0` vs. `+0` as not equal
: (value == 0 ? (1 / value == 1 / other) : value == +other);
case regexpClass:
case stringClass:
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
// treat string primitives and their corresponding object instances as equal
return value == String(other);
}
var isArr = valClass == arrayClass;
if (!isArr) {
// unwrap any `lodash` wrapped values
var valWrapped = hasOwnProperty.call(value, '__wrapped__'),
othWrapped = hasOwnProperty.call(other, '__wrapped__');
if (valWrapped || othWrapped) {
return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, callback, isWhere, stackA, stackB);
}
// exit for functions and DOM nodes
if (valClass != objectClass || (!support.nodeClass && (isNode(value) || isNode(other)))) {
return false;
}
if (!support.argsObject) {
valIsArg = isArguments(value);
othIsArg = isArguments(other);
}
var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'),
hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor');
if (hasValCtor != hasOthCtor) {
return false;
}
if (!hasValCtor) {
// in older versions of Opera, `arguments` objects have `Array` constructors
var valCtor = valIsArg ? Object : value.constructor,
othCtor = othIsArg ? Object : other.constructor;
// non `Object` object instances with different constructors are not equal
if (valCtor != othCtor &&
!(isFunction(valCtor) && valCtor instanceof valCtor && isFunction(othCtor) && othCtor instanceof othCtor) &&
('constructor' in value && 'constructor' in other)
) {
return false;
}
}
}
// assume cyclic structures are equal
// the algorithm for detecting cyclic structures is adapted from ES 5.1
// section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
stackA || (stackA = []);
stackB || (stackB = []);
var length = stackA.length;
while (length--) {
if (stackA[length] == value) {
return stackB[length] == other;
}
}
var size = 0;
result = true;
// add `value` and `other` to the stack of traversed objects
stackA.push(value);
stackB.push(other);
// recursively compare objects and arrays (susceptible to call stack limits)
if (isArr) {
// compare lengths to determine if a deep comparison is necessary
length = value.length;
size = other.length;
result = size == length;
if (result || isWhere) {
// deep compare the contents, ignoring non-numeric properties
while (size--) {
var index = length,
othValue = other[size];
if (isWhere) {
while (index--) {
if ((result = baseIsEqual(value[index], othValue, callback, isWhere, stackA, stackB))) {
break;
}
}
} else if (!(result = baseIsEqual(value[size], othValue, callback, isWhere, stackA, stackB))) {
break;
}
}
}
}
else {
// deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
// which, in this case, is more costly
baseForIn(other, function(othValue, key, other) {
if (hasOwnProperty.call(other, key)) {
// count the number of properties.
size++;
// deep compare each property value.
return (result = hasOwnProperty.call(value, key) && baseIsEqual(value[key], othValue, callback, isWhere, stackA, stackB));
}
});
if (result && !isWhere) {
// ensure both objects have the same number of properties
baseForIn(value, function(valValue, key, value) {
if (hasOwnProperty.call(value, key)) {
// `size` will be `-1` if `value` has more properties than `other`
return (result = --size > -1);
}
});
}
}
stackA.pop();
stackB.pop();
return result;
}
/**
* The base implementation of `_.merge` without argument juggling or support
* for `this` binding.
*
* @private
* @param {Object} object The destination object.
* @param {Object} source The source object.
* @param {Function} [callback] The function to customize merging properties.
* @param {Array} [stackA=[]] Tracks traversed source objects.
* @param {Array} [stackB=[]] Associates values with source counterparts.
*/
function baseMerge(object, source, callback, stackA, stackB) {
(isArray(source) ? baseEach : baseForOwn)(source, function(source, key) {
var found,
isArr,
result = source,
value = object[key];
if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
// avoid merging previously merged cyclic sources
var stackLength = stackA.length;
while (stackLength--) {
if ((found = stackA[stackLength] == source)) {
value = stackB[stackLength];
break;
}
}
if (!found) {
var isShallow;
if (callback) {
result = callback(value, source);
if ((isShallow = typeof result != 'undefined')) {
value = result;
}
}
if (!isShallow) {
value = isArr
? (isArray(value) ? value : [])
: (isPlainObject(value) ? value : {});
}
// add `source` and associated `value` to the stack of traversed objects
stackA.push(source);
stackB.push(value);
// recursively merge objects and arrays (susceptible to call stack limits)
if (!isShallow) {
baseMerge(value, source, callback, stackA, stackB);
}
}
}
else {
if (callback) {
result = callback(value, source);
if (typeof result == 'undefined') {
result = source;
}
}
if (typeof result != 'undefined') {
value = result;
}
}
object[key] = value;
});
}
/**
* The base implementation of `_.random` without argument juggling or support
* for returning floating-point numbers.
*
* @private
* @param {number} min The minimum possible value.
* @param {number} max The maximum possible value.
* @returns {number} Returns the random number.
*/
function baseRandom(min, max) {
return min + floor(nativeRandom() * (max - min + 1));
}
/**
* The base implementation of `_.uniq` without support for callback shorthands
* or `this` binding.
*
* @private
* @param {Array} array The array to process.
* @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
* @param {Function} [callback] The function called per iteration.
* @returns {Array} Returns the new duplicate-value-free array.
*/
function baseUniq(array, isSorted, callback) {
var length = array ? array.length : 0;
if (!length) {
return [];
}
var index = -1,
indexOf = getIndexOf(),
prereq = !isSorted && indexOf === baseIndexOf,
isLarge = prereq && createCache && length >= 200,
isCommon = prereq && !isLarge,
result = [];
if (isLarge) {
var seen = createCache();
indexOf = cacheIndexOf;
} else {
seen = (callback && !isSorted) ? [] : result;
}
outer:
while (++index < length) {
var value = array[index],
computed = callback ? callback(value, index, array) : value;
if (isCommon) {
var seenIndex = seen.length;
while (seenIndex--) {
if (seen[seenIndex] === computed) {
continue outer;
}
}
if (callback) {
seen.push(computed);
}
result.push(value);
}
else if (isSorted) {
if (!index || seen !== computed) {
seen = computed;
result.push(value);
}
}
else if (indexOf(seen, computed) < 0) {
if (callback || isLarge) {
seen.push(computed);
}
result.push(value);
}
}
return result;
}
/**
* The base implementation of `_.values` and `_.valuesIn` which creates an
* array of `object` property values corresponding to the property names
* returned by `keysFunc`.
*
* @private
* @param {Object} object The object to inspect.
* @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns the array of property values.
*/
function baseValues(object, keysFunc) {
var index = -1,
props = keysFunc(object),
length = props.length,
result = Array(length);
while (++index < length) {
result[index] = object[props[index]];
}
return result;
}
/**
* Creates an array that is the composition of partially applied arguments,
* placeholders, and provided arguments into a single array of arguments.
*
* @private
* @param {Array} partialArgs An array of arguments to prepend to those provided.
* @param {Array} partialHolders An array of `partialArgs` placeholder indexes.
* @param {Array|Object} args The provided arguments.
* @returns {Array} Returns the new array of composed arguments.
*/
function composeArgs(partialArgs, partialHolders, args) {
var holdersLength = partialHolders.length,
argsIndex = -1,
argsLength = nativeMax(args.length - holdersLength, 0),
leftIndex = -1,
leftLength = partialArgs.length,
result = Array(argsLength + leftLength);
while (++leftIndex < leftLength) {
result[leftIndex] = partialArgs[leftIndex];
}
while (++argsIndex < holdersLength) {
result[partialHolders[argsIndex]] = args[argsIndex];
}
while (argsLength--) {
result[leftIndex++] = args[argsIndex++];
}
return result;
}
/**
* This function is like `composeArgs` except that the arguments composition
* is tailored for `_.partialRight`.
*
* @private
* @param {Array} partialRightArgs An array of arguments to append to those provided.
* @param {Array} partialHolders An array of `partialRightArgs` placeholder indexes.
* @param {Array|Object} args The provided arguments.
* @returns {Array} Returns the new array of composed arguments.
*/
function composeArgsRight(partialRightArgs, partialRightHolders, args) {
var holdersIndex = -1,
holdersLength = partialRightHolders.length,
argsIndex = -1,
argsLength = nativeMax(args.length - holdersLength, 0),
rightIndex = -1,
rightLength = partialRightArgs.length,
result = Array(argsLength + rightLength);
while (++argsIndex < argsLength) {
result[argsIndex] = args[argsIndex];
}
var pad = argsIndex;
while (++rightIndex < rightLength) {
result[pad + rightIndex] = partialRightArgs[rightIndex];
}
while (++holdersIndex < holdersLength) {
result[pad + partialRightHolders[holdersIndex]] = args[argsIndex++];
}
return result;
}
/**
* Creates a function that aggregates a collection, creating an object or
* array composed from the results of running each element in the collection
* through a callback. The given setter function sets the keys and values of
* the composed object or array.
*
* @private
* @param {Function} setter The setter function.
* @param {boolean} [retArray=false] A flag to indicate that the aggregator
* function should return an array.
* @returns {Function} Returns the new aggregator function.
*/
function createAggregator(setter, retArray) {
return function(collection, callback, thisArg) {
var result = retArray ? [[], []] : {};
callback = lodash.createCallback(callback, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
setter(result, value, callback(value, index, collection), collection);
}
} else {
baseEach(collection, function(value, key, collection) {
setter(result, value, callback(value, key, collection), collection);
});
}
return result;
};
}
/**
* Creates a cache object to optimize linear searches of large arrays.
*
* @private
* @param {Array} [array=[]] The array to search.
* @returns {Object} Returns the new cache object.
*/
var createCache = Set && function(array) {
var cache = new Set,
length = array ? array.length : 0;
cache.push = cache.add;
while (length--) {
cache.push(array[length]);
}
return cache;
};
/**
* Creates the pad required for `string` based on the given padding length.
* The `chars` string may be truncated if the number of padding characters
* exceeds the padding length.
*
* @private
* @param {string} string The string to create padding for.
* @param {number} [length=0] The padding length.
* @param {string} [chars=' '] The string used as padding.
* @returns {string} Returns the pad for `string`.
*/
function createPad(string, length, chars) {
var strLength = string.length;
length = +length;
if (strLength >= length || !nativeIsFinite(length)) {
return '';
}
var padLength = length - strLength;
chars = chars == null ? ' ' : String(chars);
return repeat(chars, ceil(padLength / chars.length)).slice(0, padLength);
}
/**
* Creates a function that either curries or invokes `func` with an optional
* `this` binding and partially applied arguments.
*
* @private
* @param {Function|string} func The function or method name to reference.
* @param {number} bitmask The bitmask of flags to compose.
* The bitmask may be composed of the following flags:
* 1 - `_.bind`
* 2 - `_.bindKey`
* 4 - `_.curry`
* 8 - `_.curry` (bound)
* 16 - `_.partial`
* 32 - `_.partialRight`
* @param {number} [arity] The arity of `func`.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {Array} [partialArgs] An array of arguments to prepend to those
* provided to the new function.
* @param {Array} [partialRightArgs] An array of arguments to append to those
* provided to the new function.
* @returns {Function} Returns the new function.
*/
function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs) {
var isBind = bitmask & BIND_FLAG,
isBindKey = bitmask & BIND_KEY_FLAG,
isPartial = bitmask & PARTIAL_FLAG,
isPartialRight = bitmask & PARTIAL_RIGHT_FLAG;
if (!isBindKey && !isFunction(func)) {
throw new TypeError;
}
if (isPartial && !partialArgs.length) {
bitmask &= ~PARTIAL_FLAG;
isPartial = partialArgs = false;
}
if (isPartialRight && !partialRightArgs.length) {
bitmask &= ~PARTIAL_RIGHT_FLAG;
isPartialRight = partialRightArgs = false;
}
var data = !isBindKey && func[expando];
if (data && data !== true) {
// shallow clone `data`
data = slice(data);
// clone partial left arguments
if (data[4]) {
data[4] = slice(data[4]);
}
// clone partial right arguments
if (data[5]) {
data[5] = slice(data[5]);
}
// set arity if provided
if (typeof arity == 'number') {
data[2] = arity;
}
// set `thisArg` if not previously bound
var bound = data[1] & BIND_FLAG;
if (isBind && !bound) {
data[3] = thisArg;
}
// set if currying a bound function
if (!isBind && bound) {
bitmask |= CURRY_BOUND_FLAG;
}
// append partial left arguments
if (isPartial) {
if (data[4]) {
push.apply(data[4], partialArgs);
} else {
data[4] = partialArgs;
}
}
// prepend partial right arguments
if (isPartialRight) {
if (data[5]) {
unshift.apply(data[5], partialRightArgs);
} else {
data[5] = partialRightArgs;
}
}
// merge flags
data[1] |= bitmask;
return createWrapper.apply(null, data);
}
if (isPartial) {
var partialHolders = getHolders(partialArgs);
}
if (isPartialRight) {
var partialRightHolders = getHolders(partialRightArgs);
}
if (arity == null) {
arity = isBindKey ? 0 : func.length;
}
arity = nativeMax(arity, 0);
// fast path for `_.bind`
data = [func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders];
return (bitmask == BIND_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG))
? baseBind(data)
: baseCreateWrapper(data);
}
/**
* Finds the indexes of all placeholder elements in `array`.
*
* @private
* @param {Array} array The array to inspect.
* @returns {Array} Returns the new array of placeholder indexes.
*/
function getHolders(array) {
var index = -1,
length = array.length,
result = [];
while (++index < length) {
if (array[index] === lodash) {
result.push(index);
}
}
return result;
}
/**
* Gets the appropriate "indexOf" function. If the `_.indexOf` method is
* customized this function returns the custom method, otherwise it returns
* the `baseIndexOf` function.
*
* @private
* @returns {Function} Returns the "indexOf" function.
*/
function getIndexOf() {
var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
return result;
}
/**
* Checks if `value` is a native function.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a native function, else `false`.
*/
function isNative(value) {
return typeof value == 'function' && reNative.test(fnToString.call(value));
}
/**
* Sets wrapper metadata on a given function.
*
* @private
* @param {Function} func The function to set data on.
* @param {Array} value The data array to set.
*/
var setData = !defineProperty ? noop : function(func, value) {
descriptor.value = value;
defineProperty(func, expando, descriptor);
};
/**
* A fallback implementation of `_.isPlainObject` which checks if `value` is
* an object created by the `Object` constructor, assuming objects created
* by the `Object` constructor have no inherited enumerable properties and
* that there are no `Object.prototype` extensions.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
*/
function shimIsPlainObject(value) {
var ctor,
result;
// avoid non `Object` objects, `arguments` objects, and DOM elements
if (!(value && toString.call(value) == objectClass) ||
(!hasOwnProperty.call(value, 'constructor') &&
(ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) ||
(!support.argsClass && isArguments(value)) ||
(!support.nodeClass && isNode(value))) {
return false;
}
// IE < 9 iterates inherited properties before own properties. If the first
// iterated property is an object's own property then there are no inherited
// enumerable properties.
if (support.ownLast) {
baseForIn(value, function(value, key, object) {
result = hasOwnProperty.call(object, key);
return false;
});
return result !== false;
}
// In most environments an object's own properties are iterated before
// its inherited properties. If the last iterated property is an object's
// own property then there are no inherited enumerable properties.
baseForIn(value, function(value, key) {
result = key;
});
return typeof result == 'undefined' || hasOwnProperty.call(value, result);
}
/**
* A fallback implementation of `Object.keys` which creates an array of the
* own enumerable property names of `object`.
*
* @private
* @param {Object} object The object to inspect.
* @returns {Array} Returns the array of property names.
*/
function shimKeys(object) {
var keyIndex,
index = -1,
props = keysIn(object),
length = props.length,
objLength = length && object.length,
maxIndex = objLength - 1,
result = [];
var allowIndexes = typeof objLength == 'number' && objLength > 0 &&
(isArray(object) || (support.nonEnumArgs && isArguments(object)) ||
(support.nonEnumStrings && isString(object)));
while (++index < length) {
var key = props[index];
Eif ((allowIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0)) ||
hasOwnProperty.call(object, key)) {
result.push(key);
}
}
return result;
}
/*--------------------------------------------------------------------------*/
/**
* Creates an array with all falsey values removed. The values `false`, `null`,
* `0`, `""`, `undefined`, and `NaN` are all falsey.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to compact.
* @returns {Array} Returns the new array of filtered values.
* @example
*
* _.compact([0, 1, false, 2, '', 3]);
* // => [1, 2, 3]
*/
function compact(array) {
var index = -1,
length = array ? array.length : 0,
resIndex = 0,
result = [];
while (++index < length) {
var value = array[index];
if (value) {
result[resIndex++] = value;
}
}
return result;
}
/**
* Creates an array excluding all values of the provided arrays using strict
* equality for comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to process.
* @param {...Array} [values] The arrays of values to exclude.
* @returns {Array} Returns the new array of filtered values.
* @example
*
* _.difference([1, 2, 3], [5, 2, 10]);
* // => [1, 3]
*/
function difference() {
return baseDifference(arguments[0], baseFlatten(arguments, true, true, 1));
}
/**
* Creates a slice of `array` with `n` elements dropped from the beginning.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {number} [n=1] The number of elements to drop.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.drop([1, 2, 3], 1);
* // => [2, 3]
*
* _.drop([1, 2, 3], 2);
* // => [3]
*
* _.drop([1, 2, 3], 5);
* // => []
*
* _.drop([1, 2, 3], 0);
* // => [1, 2, 3]
*/
var drop = rest;
/**
* Creates a slice of `array` with `n` elements dropped from the end.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {number} [n=1] The number of elements to drop.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.dropRight([1, 2, 3], 1);
* // => [1, 2]
*
* _.dropRight([1, 2, 3], 2);
* // => [1]
*
* _.dropRight([1, 2, 3], 5);
* // => []
*
* _.dropRight([1, 2, 3], 0);
* // => [1, 2, 3]
*/
var dropRight = initial;
/**
* Creates a slice of `array` excluding elements dropped from the end.
* Elements will be dropped until the predicate returns falsey. The predicate
* is bound to `thisArg` and invoked with three arguments; (value, index, array).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {Function|Object|string} [predicate=identity] The function called
* per element.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.dropRightWhile([1, 2, 3], function(num) {
* return num > 1;
* });
* // => [1]
*
* var characters = [
* { 'name': 'barney', 'employer': 'slate' },
* { 'name': 'fred', 'employer': 'slate', 'blocked': true },
* { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.pluck(_.dropRightWhile(characters, 'blocked'), 'name');
* // => ['barney']
*
* // using "_.where" callback shorthand
* _.pluck(_.dropRightWhile(characters, { 'employer': 'na' }), 'name');
* // => ['barney', 'fred']
*/
var dropRightWhile = initial;
/**
* Creates a slice of `array` excluding elements dropped from the beginning.
* Elements will be dropped until the predicate returns falsey. The predicate
* is bound to `thisArg` and invoked with three arguments; (value, index, array).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {Function|Object|string} [predicate=identity] The function called
* per element.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.dropWhile([1, 2, 3], function(num) {
* return num < 3;
* });
* // => [3]
*
* var characters = [
* { 'name': 'barney', 'employer': 'slate', 'blocked': true },
* { 'name': 'fred', 'employer': 'slate' },
* { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.pluck(_.dropWhile(characters, 'blocked'), 'name');
* // => ['fred', 'pebbles']
*
* // using "_.where" callback shorthand
* _.pluck(_.dropWhile(characters, { 'employer': 'slate' }), 'name');
* // => ['pebbles']
*/
var dropWhile = rest;
/**
* This method is like `_.find` except that it returns the index of the first
* element the predicate returns truthy for, instead of the element itself.
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40, 'blocked': true },
* { 'name': 'pebbles', 'age': 1 }
* ];
*
* _.findIndex(characters, function(chr) {
* return chr.age < 20;
* });
* // => 2
*
* // using "_.where" callback shorthand
* _.findIndex(characters, { 'age': 36 });
* // => 0
*
* // using "_.pluck" callback shorthand
* _.findIndex(characters, 'blocked');
* // => 1
*/
function findIndex(array, predicate, thisArg) {
var index = -1,
length = array ? array.length : 0;
predicate = lodash.createCallback(predicate, thisArg, 3);
while (++index < length) {
if (predicate(array[index], index, array)) {
return index;
}
}
return -1;
}
/**
* This method is like `_.findIndex` except that it iterates over elements
* of a collection from right to left.
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36, 'blocked': true },
* { 'name': 'fred', 'age': 40 },
* { 'name': 'pebbles', 'age': 1, 'blocked': true }
* ];
*
* _.findLastIndex(characters, function(chr) {
* return chr.age > 30;
* });
* // => 1
*
* // using "_.where" callback shorthand
* _.findLastIndex(characters, { 'age': 36 });
* // => 0
*
* // using "_.pluck" callback shorthand
* _.findLastIndex(characters, 'blocked');
* // => 2
*/
function findLastIndex(array, predicate, thisArg) {
var length = array ? array.length : 0;
predicate = lodash.createCallback(predicate, thisArg, 3);
while (length--) {
if (predicate(array[length], length, array)) {
return length;
}
}
return -1;
}
/**
* Gets the first element of `array`.
*
* Note: The `n` and `predicate` arguments are deprecated; replace with
* `_.take` and `_.takeWhile` respectively.
*
* @static
* @memberOf _
* @alias head
* @category Arrays
* @param {Array} array The array to query.
* @returns {*} Returns the first element of `array`.
* @example
*
* _.first([1, 2, 3]);
* // => 1
*
* _.first([]);
* // => undefined
*/
function first(array, predicate, thisArg) {
if (typeof predicate != 'number' && predicate != null) {
var index = -1,
length = array ? array.length : 0,
n = 0;
predicate = lodash.createCallback(predicate, thisArg, 3);
while (++index < length && predicate(array[index], index, array)) {
n++;
}
} else {
n = predicate;
if (n == null || thisArg) {
return array ? array[0] : undefined;
}
}
return slice(array, 0, n < 0 ? 0 : n);
}
/**
* Flattens a nested array (the nesting can be to any depth). If `isShallow`
* is truthy, the array will only be flattened a single level. If a callback
* is provided each element of the array is passed through the callback before
* flattening. The callback is bound to `thisArg` and invoked with three
* arguments; (value, index, array).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to flatten.
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
* @param {Function|Object|string} [callback] The function called per iteration.
* If a property name or object is provided it will be used to create a "_.pluck"
* or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns the new flattened array.
* @example
*
* _.flatten([1, [2], [3, [[4]]]]);
* // => [1, 2, 3, 4];
*
* // using `isShallow`
* _.flatten([1, [2], [3, [[4]]]], true);
* // => [1, 2, 3, [[4]]];
*
* var characters = [
* { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
* { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
* ];
*
* // using "_.pluck" callback shorthand
* _.flatten(characters, 'pets');
* // => ['hoppy', 'baby puss', 'dino']
*/
function flatten(array, isShallow, callback, thisArg) {
var length = array ? array.length : 0;
if (!length) {
return [];
}
// juggle arguments
var type = typeof isShallow;
if (type != 'boolean' && isShallow != null) {
thisArg = callback;
callback = isShallow;
isShallow = false;
// enables use as a callback for functions like `_.map`
if ((type == 'number' || type == 'string') && thisArg && thisArg[callback] === array) {
callback = null;
}
}
if (callback != null) {
array = map(array, callback, thisArg);
}
return baseFlatten(array, isShallow);
}
/**
* Gets the index at which the first occurrence of `value` is found using
* strict equality for comparisons, i.e. `===`. If the array is already sorted
* providing `true` for `fromIndex` will run a faster binary search.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
* @param {*} value The value to search for.
* @param {boolean|number} [fromIndex=0] The index to search from or `true`
* to perform a binary search on a sorted array.
* @returns {number} Returns the index of the matched value, else `-1`.
* @example
*
* _.indexOf([1, 2, 3, 1, 2, 3], 2);
* // => 1
*
* // using `fromIndex`
* _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
* // => 4
*
* // performing a binary search
* _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
* // => 2
*/
function indexOf(array, value, fromIndex) {
var length = array ? array.length : 0;
if (typeof fromIndex == 'number') {
fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
} else if (fromIndex) {
var index = sortedIndex(array, value);
return (length && array[index] === value) ? index : -1;
}
return baseIndexOf(array, value, fromIndex);
}
/**
* Gets all but the last element of `array`.
*
* Note: The `n` and `predicate` arguments are deprecated; replace with
* `_.dropRight` and `_.dropRightWhile` respectively.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to query.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.initial([1, 2, 3]);
* // => [1, 2]
*/
function initial(array, predicate, thisArg) {
var length = array ? array.length : 0;
if (typeof predicate != 'number' && predicate != null) {
var index = length,
n = 0;
predicate = lodash.createCallback(predicate, thisArg, 3);
while (index-- && predicate(array[index], index, array)) {
n++;
}
} else {
n = (predicate == null || thisArg) ? 1 : predicate;
}
n = length - (n || 0);
return slice(array, 0, n < 0 ? 0 : n);
}
/**
* Creates an array of unique values present in all provided arrays using
* strict equality for comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {...Array} [arrays] The arrays to inspect.
* @returns {Array} Returns the new array of shared values.
* @example
*
* _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
* // => [1, 2]
*/
function intersection() {
var args = [],
argsIndex = -1,
argsLength = arguments.length,
caches = [],
indexOf = getIndexOf(),
prereq = createCache && indexOf === baseIndexOf;
while (++argsIndex < argsLength) {
var value = arguments[argsIndex];
if (isArray(value) || isArguments(value)) {
args.push(value);
caches.push(prereq && value.length >= 120 &&
createCache(argsIndex && value));
}
}
argsLength = args.length;
var array = args[0],
index = -1,
length = array ? array.length : 0,
result = [],
seen = caches[0];
outer:
while (++index < length) {
value = array[index];
if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value)) < 0) {
argsIndex = argsLength;
while (--argsIndex) {
var cache = caches[argsIndex];
if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
continue outer;
}
}
if (seen) {
seen.push(value);
}
result.push(value);
}
}
return result;
}
/**
* Gets the last element of `array`.
*
* Note: The `n` and `predicate` arguments are deprecated; replace with
* `_.takeRight` and `_.takeRightWhile` respectively.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to query.
* @returns {*} Returns the last element of `array`.
* @example
*
* _.last([1, 2, 3]);
* // => 3
*/
function last(array, predicate, thisArg) {
var length = array ? array.length : 0;
if (typeof predicate != 'number' && predicate != null) {
var index = length,
n = 0;
predicate = lodash.createCallback(predicate, thisArg, 3);
while (index-- && predicate(array[index], index, array)) {
n++;
}
} else {
n = predicate;
if (n == null || thisArg) {
return array ? array[length - 1] : undefined;
}
}
n = length - (n || 0);
return slice(array, n < 0 ? 0 : n);
}
/**
* Gets the index at which the last occurrence of `value` is found using
* strict equality for comparisons, i.e. `===`. If `fromIndex` is negative,
* it is used as the offset from the end of the collection.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
* @param {*} value The value to search for.
* @param {number} [fromIndex=array.length-1] The index to search from.
* @returns {number} Returns the index of the matched value, else `-1`.
* @example
*
* _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
* // => 4
*
* // using `fromIndex`
* _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
* // => 1
*/
function lastIndexOf(array, value, fromIndex) {
var index = array ? array.length : 0;
if (typeof fromIndex == 'number') {
index = (fromIndex < 0 ? nativeMax(index + fromIndex, 0) : nativeMin(fromIndex || 0, index - 1)) + 1;
}
while (index--) {
if (array[index] === value) {
return index;
}
}
return -1;
}
/**
* Removes all provided values from `array` using strict equality for
* comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to modify.
* @param {...*} [values] The values to remove.
* @returns {Array} Returns `array`.
* @example
*
* var array = [1, 2, 3, 1, 2, 3];
* _.pull(array, 2, 3);
* console.log(array);
* // => [1, 1]
*/
function pull(array) {
var argsIndex = 0,
argsLength = arguments.length,
length = array ? array.length : 0;
while (++argsIndex < argsLength) {
var index = -1,
value = arguments[argsIndex];
while (++index < length) {
if (array[index] === value) {
splice.call(array, index--, 1);
length--;
}
}
}
return array;
}
/**
* Removes all elements from an array that the predicate returns truthy for
* and returns an array of removed elements. The predicate is bound to `thisArg`
* and invoked with three arguments; (value, index, array).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to modify.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {Array} Returns the array of removed elements.
* @example
*
* var array = [1, 2, 3, 4, 5, 6];
* var evens = _.remove(array, function(num) { return num % 2 == 0; });
*
* console.log(array);
* // => [1, 3, 5]
*
* console.log(evens);
* // => [2, 4, 6]
*/
function remove(array, predicate, thisArg) {
var index = -1,
length = array ? array.length : 0,
result = [];
predicate = lodash.createCallback(predicate, thisArg, 3);
while (++index < length) {
var value = array[index];
if (predicate(value, index, array)) {
result.push(value);
splice.call(array, index--, 1);
length--;
}
}
return result;
}
/**
* Gets all but the first element of `array`.
*
* Note: The `n` and `predicate` arguments are deprecated; replace with
* `_.drop` and `_.dropWhile` respectively.
*
* @static
* @memberOf _
* @alias tail
* @category Arrays
* @param {Array} array The array to query.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.rest([1, 2, 3]);
* // => [2, 3]
*/
function rest(array, predicate, thisArg) {
if (typeof predicate != 'number' && predicate != null) {
var index = -1,
length = array ? array.length : 0,
n = 0;
predicate = lodash.createCallback(predicate, thisArg, 3);
while (++index < length && predicate(array[index], index, array)) {
n++;
}
} else if (predicate == null || thisArg) {
n = 1;
} else {
n = predicate < 0 ? 0 : predicate;
}
return slice(array, n);
}
/**
* Slices `array` from the `start` index up to, but not including, the `end` index.
*
* Note: This function is used instead of `Array#slice` to support node lists
* in IE < 9 and to ensure dense arrays are returned.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to slice.
* @param {number} [start=0] The start index.
* @param {number} [end=array.length] The end index.
* @returns {Array} Returns the slice of `array`.
*/
function slice(array, start, end) {
var index = -1,
length = array ? array.length : 0;
start = typeof start == 'undefined' ? 0 : (+start || 0);
if (start < 0) {
start = nativeMax(length + start, 0);
} else if (start > length) {
start = length;
}
end = typeof end == 'undefined' ? length : (+end || 0);
if (end < 0) {
end = nativeMax(length + end, 0);
} else if (end > length) {
end = length;
}
length = start > end ? 0 : (end - start);
var result = Array(length);
while (++index < length) {
result[index] = array[start + index];
}
return result;
}
/**
* Uses a binary search to determine the smallest index at which a value
* should be inserted into a given sorted array in order to maintain the sort
* order of the array. If a callback is provided it will be executed for
* `value` and each element of `array` to compute their sort ranking. The
* callback is bound to `thisArg` and invoked with one argument; (value).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to inspect.
* @param {*} value The value to evaluate.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {number} Returns the index at which `value` should be inserted
* into `array`.
* @example
*
* _.sortedIndex([20, 30, 50], 40);
* // => 2
*
* var dict = {
* 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
* };
*
* // using `callback`
* _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
* return dict.wordToNumber[word];
* });
* // => 2
*
* // using `callback` with `thisArg`
* _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
* return this.wordToNumber[word];
* }, dict);
* // => 2
*
* // using "_.pluck" callback shorthand
* _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
* // => 2
*/
function sortedIndex(array, value, callback, thisArg) {
var low = 0,
high = array ? array.length : low;
// explicitly reference `identity` for better inlining in Firefox
callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
value = callback(value);
while (low < high) {
var mid = (low + high) >>> 1;
(callback(array[mid]) < value)
? (low = mid + 1)
: (high = mid);
}
return low;
}
/**
* Creates a slice of `array` with `n` elements taken from the beginning.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {number} [n=1] The number of elements to take.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.take([1, 2, 3], 1);
* // => [1]
*
* _.take([1, 2, 3], 2);
* // => [1, 2]
*
* _.take([1, 2, 3], 5);
* // => [1, 2, 3]
*
* _.take([1, 2, 3], 0);
* // => []
*/
var take = first;
/**
* Creates a slice of `array` with `n` elements taken from the end.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {number} [n=1] The number of elements to take.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.takeRight([1, 2, 3], 1);
* // => [3]
*
* _.takeRight([1, 2, 3], 2);
* // => [2, 3]
*
* _.takeRight([1, 2, 3], 5);
* // => [1, 2, 3]
*
* _.takeRight([1, 2, 3], 0);
* // => []
*/
var takeRight = last;
/**
* Creates a slice of `array` with elements taken from the end. Elements will
* be taken until the predicate returns falsey. The predicate is bound to
* `thisArg` and invoked with three arguments; (value, index, array).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {Function|Object|string} [predicate=identity] The function called
* per element.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.takeRightWhile([1, 2, 3], function(num) {
* return num > 1;
* });
* // => [2, 3]
*
* var characters = [
* { 'name': 'barney', 'employer': 'slate' },
* { 'name': 'fred', 'employer': 'slate', 'blocked': true },
* { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.pluck(_.takeRightWhile(characters, 'blocked'), 'name');
* // => ['fred', 'pebbles']
*
* // using "_.where" callback shorthand
* _.pluck(_.takeRightWhile(characters, { 'employer': 'na' }), 'name');
* // => ['pebbles']
*/
var takeRightWhile = last;
/**
* Creates a slice of `array` with elements taken from the beginning. Elements
* will be taken until the predicate returns falsey. The predicate is bound
* to `thisArg` and invoked with three arguments; (value, index, array).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @type Function
* @category Arrays
* @param {Array} array The array to query.
* @param {Function|Object|string} [predicate=identity] The function called
* per element.
* @returns {Array} Returns the slice of `array`.
* @example
*
* _.takeWhile([1, 2, 3], function(num) {
* return num < 3;
* });
* // => [1, 2]
*
* var characters = [
* { 'name': 'barney', 'employer': 'slate', 'blocked': true },
* { 'name': 'fred', 'employer': 'slate' },
* { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.pluck(_.takeWhile(characters, 'blocked'), 'name');
* // => ['barney']
*
* // using "_.where" callback shorthand
* _.pluck(_.takeWhile(characters, { 'employer': 'slate' }), 'name');
* // => ['barney', 'fred']
*/
var takeWhile = first;
/**
* Creates an array of unique values, in order, of the provided arrays using
* strict equality for comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {...Array} [arrays] The arrays to inspect.
* @returns {Array} Returns the new array of combined values.
* @example
*
* _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
* // => [1, 2, 3, 5, 4]
*/
function union() {
return baseUniq(baseFlatten(arguments, true, true));
}
/**
* Creates a duplicate-value-free version of an array using strict equality
* for comparisons, i.e. `===`. If the array is sorted, providing
* `true` for `isSorted` will use a faster algorithm. If a callback is provided
* each element of `array` is passed through the callback before uniqueness
* is computed. The callback is bound to `thisArg` and invoked with three
* arguments; (value, index, array).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @alias unique
* @category Arrays
* @param {Array} array The array to process.
* @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
* @param {Function|Object|string} [callback] The function called per iteration.
* If a property name or object is provided it will be used to create a "_.pluck"
* or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns the new duplicate-value-free array.
* @example
*
* _.uniq([1, 2, 1, 3, 1]);
* // => [1, 2, 3]
*
* // using `isSorted`
* _.uniq([1, 1, 2, 2, 3], true);
* // => [1, 2, 3]
*
* // using `callback`
* _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
* // => ['A', 'b', 'C']
*
* // using `callback` with `thisArg`
* _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
* // => [1, 2.5, 3]
*
* // using "_.pluck" callback shorthand
* _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
* // => [{ 'x': 1 }, { 'x': 2 }]
*/
function uniq(array, isSorted, callback, thisArg) {
var length = array ? array.length : 0;
if (!length) {
return [];
}
// juggle arguments
var type = typeof isSorted;
if (type != 'boolean' && isSorted != null) {
thisArg = callback;
callback = isSorted;
isSorted = false;
// enables use as a callback for functions like `_.map`
if ((type == 'number' || type == 'string') && thisArg && thisArg[callback] === array) {
callback = null;
}
}
if (callback != null) {
callback = lodash.createCallback(callback, thisArg, 3);
}
return baseUniq(array, isSorted, callback);
}
/**
* Creates an array excluding all provided values using strict equality for
* comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to filter.
* @param {...*} [values] The values to exclude.
* @returns {Array} Returns the new array of filtered values.
* @example
*
* _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
* // => [2, 3, 4]
*/
function without() {
return baseDifference(arguments[0], slice(arguments, 1));
}
/**
* Creates an array that is the symmetric difference of the provided arrays.
* See [Wikipedia](http://en.wikipedia.org/wiki/Symmetric_difference) for
* more details.
*
* @static
* @memberOf _
* @category Arrays
* @param {...Array} [arrays] The arrays to inspect.
* @returns {Array} Returns the new array of values.
* @example
*
* _.xor([1, 2, 3], [5, 2, 1, 4]);
* // => [3, 5, 4]
*
* _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
* // => [1, 4, 5]
*/
function xor() {
var index = -1,
length = arguments.length;
while (++index < length) {
var array = arguments[index];
if (isArray(array) || isArguments(array)) {
var result = result
? baseDifference(result, array).concat(baseDifference(array, result))
: array;
}
}
return result ? baseUniq(result) : [];
}
/**
* Creates an array of grouped elements, the first of which contains the first
* elements of the given arrays, the second of which contains the second elements
* of the given arrays, and so on. If a zipped value is provided its corresponding
* unzipped value will be returned.
*
* @static
* @memberOf _
* @alias unzip
* @category Arrays
* @param {...Array} [arrays] The arrays to process.
* @returns {Array} Returns the array of grouped elements.
* @example
*
* _.zip(['fred', 'barney'], [30, 40], [true, false]);
* // => [['fred', 30, true], ['barney', 40, false]]
*
* _.unzip([['fred', 30, true], ['barney', 40, false]]);
* // => [['fred', 'barney'], [30, 40], [true, false]]
*/
function zip() {
var array = arguments.length > 1 ? arguments : arguments[0],
index = -1,
length = array ? max(pluck(array, 'length')) : 0,
result = Array(length < 0 ? 0 : length);
while (++index < length) {
result[index] = pluck(array, index);
}
return result;
}
/**
* Creates an object composed from arrays of `keys` and `values`. Provide
* either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
* or two arrays, one of `keys` and one of corresponding `values`.
*
* @static
* @memberOf _
* @alias object
* @category Arrays
* @param {Array} keys The array of keys.
* @param {Array} [values=[]] The array of values.
* @returns {Object} Returns the new object.
* @example
*
* _.zipObject(['fred', 'barney'], [30, 40]);
* // => { 'fred': 30, 'barney': 40 }
*/
function zipObject(keys, values) {
var index = -1,
length = keys ? keys.length : 0,
result = {};
if (!values && length && !isArray(keys[0])) {
values = [];
}
while (++index < length) {
var key = keys[index];
if (values) {
result[key] = values[index];
} else if (key) {
result[key[0]] = key[1];
}
}
return result;
}
/*--------------------------------------------------------------------------*/
/**
* Creates a `lodash` object that wraps `value` with explicit method
* chaining enabled.
*
* @static
* @memberOf _
* @category Chaining
* @param {*} value The value to wrap.
* @returns {Object} Returns the new wrapper object.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 },
* { 'name': 'pebbles', 'age': 1 }
* ];
*
* var youngest = _.chain(characters)
* .sortBy('age')
* .map(function(chr) { return chr.name + ' is ' + chr.age; })
* .first()
* .value();
* // => 'pebbles is 1'
*/
function chain(value) {
return new lodashWrapper(value, true);
}
/**
* This method invokes `interceptor` and returns `value`. The interceptor is
* bound to `thisArg` and invoked with one argument; (value). The purpose of
* this method is to "tap into" a method chain in order to perform operations
* on intermediate results within the chain.
*
* @static
* @memberOf _
* @category Chaining
* @param {*} value The value to provide to `interceptor`.
* @param {Function} interceptor The function to invoke.
* @param {*} [thisArg] The `this` binding of `interceptor`.
* @returns {*} Returns `value`.
* @example
*
* _([1, 2, 3, 4])
* .tap(function(array) { array.pop(); })
* .reverse()
* .value();
* // => [3, 2, 1]
*/
function tap(value, interceptor, thisArg) {
interceptor.call(thisArg, value);
return value;
}
/**
* Enables explicit method chaining on the wrapper object.
*
* @name chain
* @memberOf _
* @category Chaining
* @returns {*} Returns the wrapper object.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* // without explicit chaining
* _(characters).first();
* // => { 'name': 'barney', 'age': 36 }
*
* // with explicit chaining
* _(characters).chain()
* .first()
* .pick('age')
* .value();
* // => { 'age': 36 }
*/
function wrapperChain() {
this.__chain__ = true;
return this;
}
/**
* Produces the result of coercing the wrapped value to a string.
*
* @name toString
* @memberOf _
* @category Chaining
* @returns {string} Returns the coerced string value.
* @example
*
* _([1, 2, 3]).toString();
* // => '1,2,3'
*/
function wrapperToString() {
return String(this.__wrapped__);
}
/**
* Extracts the wrapped value.
*
* @name valueOf
* @memberOf _
* @alias toJSON, value
* @category Chaining
* @returns {*} Returns the wrapped value.
* @example
*
* _([1, 2, 3]).valueOf();
* // => [1, 2, 3]
*/
function wrapperValueOf() {
return this.__wrapped__;
}
/*--------------------------------------------------------------------------*/
/**
* Creates an array of elements from the specified indexes, or keys, of the
* `collection`. Indexes may be specified as individual arguments or as arrays
* of indexes.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {...(number|number[]|string|string[])} [index] The indexes to retrieve,
* specified as individual indexes or arrays of indexes.
* @returns {Array} Returns the array of picked elements.
* @example
*
* _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
* // => ['a', 'c', 'e']
*
* _.at(['fred', 'barney', 'pebbles'], 0, 2);
* // => ['fred', 'pebbles']
*/
function at(collection, guard) {
var args = arguments,
index = -1,
props = baseFlatten(args, true, false, 1),
length = props.length,
type = typeof guard;
// enables use as a callback for functions like `_.map`
if ((type == 'number' || type == 'string') && args[2] && args[2][guard] === collection) {
length = 1;
}
if (support.unindexedChars && isString(collection)) {
collection = collection.split('');
}
var result = Array(length);
while(++index < length) {
result[index] = collection[props[index]];
}
return result;
}
/**
* Checks if a given value is present in a collection using strict equality
* for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
* offset from the end of the collection.
*
* @static
* @memberOf _
* @alias include
* @category Collections
* @param {Array|Object|string} collection The collection to search.
* @param {*} target The value to check for.
* @param {number} [fromIndex=0] The index to search from.
* @returns {boolean} Returns `true` if the target element is found, else `false`.
* @example
*
* _.contains([1, 2, 3], 1);
* // => true
*
* _.contains([1, 2, 3], 1, 2);
* // => false
*
* _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
* // => true
*
* _.contains('pebbles', 'eb');
* // => true
*/
function contains(collection, target, fromIndex) {
var length = collection ? collection.length : 0;
if (!(typeof length == 'number' && length > -1 && length <= maxSafeInteger)) {
var props = keys(collection);
length = props.length;
}
if (typeof fromIndex == 'number') {
fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
} else {
fromIndex = 0;
}
if (props) {
while (fromIndex < length) {
var value = collection[props[fromIndex++]];
if (value === target) {
return true;
}
}
return false;
}
if (typeof collection == 'string' || !isArray(collection) && isString(collection)) {
if (fromIndex >= length) {
return false;
}
return nativeContains
? nativeContains.call(collection, target, fromIndex)
: collection.indexOf(target, fromIndex) > -1;
}
var indexOf = getIndexOf();
return indexOf(collection, target, fromIndex) > -1;
}
/**
* Creates an object composed of keys generated from the results of running
* each element of `collection` through the callback. The corresponding value
* of each key is the number of times the key was returned by the callback.
* The callback is bound to `thisArg` and invoked with three arguments;
* (value, index|key, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
*
* _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
* // => { '4': 1, '6': 2 }
*
* _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
* // => { '4': 1, '6': 2 }
*
* _.countBy(['one', 'two', 'three'], 'length');
* // => { '3': 2, '5': 1 }
*/
var countBy = createAggregator(function(result, value, key) {
(hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
});
/**
* Checks if the predicate returns truthy for **all** elements of a collection.
* The predicate is bound to `thisArg` and invoked with three arguments;
* (value, index|key, collection).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @alias all
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {boolean} Returns `true` if all elements passed the predicate check,
* else `false`.
* @example
*
* _.every([true, 1, null, 'yes']);
* // => false
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* // using "_.pluck" callback shorthand
* _.every(characters, 'age');
* // => true
*
* // using "_.where" callback shorthand
* _.every(characters, { 'age': 36 });
* // => false
*/
function every(collection, predicate, thisArg) {
var result = true;
predicate = lodash.createCallback(predicate, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
if (!predicate(collection[index], index, collection)) {
return false;
}
}
} else {
baseEach(collection, function(value, index, collection) {
return (result = !!predicate(value, index, collection));
});
}
return result;
}
/**
* Iterates over elements of a collection returning an array of all elements
* the predicate returns truthy for. The predicate is bound to `thisArg` and
* invoked with three arguments; (value, index|key, collection).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @alias select
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {Array} Returns the new filtered array.
* @example
*
* var evens = _.filter([1, 2, 3, 4], function(num) { return num % 2 == 0; });
* // => [2, 4]
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40, 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.filter(characters, 'blocked');
* // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
*
* // using "_.where" callback shorthand
* _.filter(characters, { 'age': 36 });
* // => [{ 'name': 'barney', 'age': 36 }]
*/
function filter(collection, predicate, thisArg) {
var result = [];
predicate = lodash.createCallback(predicate, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
if (predicate(value, index, collection)) {
result.push(value);
}
}
} else {
baseEach(collection, function(value, index, collection) {
if (predicate(value, index, collection)) {
result.push(value);
}
});
}
return result;
}
/**
* Iterates over elements of a collection, returning the first element that
* the predicate returns truthy for. The predicate is bound to `thisArg` and
* invoked with three arguments; (value, index|key, collection).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @alias detect, findWhere
* @category Collections
* @param {Array|Object|string} collection The collection to search.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {*} Returns the found element, else `undefined`.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40, 'blocked': true },
* { 'name': 'pebbles', 'age': 1 }
* ];
*
* _.find(characters, function(chr) {
* return chr.age < 40;
* });
* // => { 'name': 'barney', 'age': 36 }
*
* // using "_.where" callback shorthand
* _.find(characters, { 'age': 1 });
* // => { 'name': 'pebbles', 'age': 1 }
*
* // using "_.pluck" callback shorthand
* _.find(characters, 'blocked');
* // => { 'name': 'fred', 'age': 40, 'blocked': true }
*/
function find(collection, predicate, thisArg) {
if (isArray(collection)) {
var index = findIndex(collection, predicate, thisArg);
return index > -1 ? collection[index] : undefined;
}
predicate = lodash.createCallback(predicate, thisArg, 3);
return baseFind(collection, predicate, baseEach);
}
/**
* This method is like `_.find` except that it iterates over elements of a
* collection from right to left.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to search.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {*} Returns the found element, else `undefined`.
* @example
*
* _.findLast([1, 2, 3, 4], function(num) {
* return num % 2 == 1;
* });
* // => 3
*/
function findLast(collection, predicate, thisArg) {
predicate = lodash.createCallback(predicate, thisArg, 3);
return baseFind(collection, predicate, baseEachRight);
}
/**
* Iterates over elements of a collection executing the callback for each
* element. The callback is bound to `thisArg` and invoked with three arguments;
* (value, index|key, collection). Callbacks may exit iteration early by
* explicitly returning `false`.
*
* Note: As with other "Collections" methods, objects with a `length` property
* are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
* may be used for object iteration.
*
* @static
* @memberOf _
* @alias each
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|string} Returns `collection`.
* @example
*
* _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
* // => logs each number and returns '1,2,3'
*
* _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
* // => logs each number and returns the object (property order is not guaranteed across environments)
*/
function forEach(collection, callback, thisArg) {
if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
if (callback(collection[index], index, collection) === false) {
break;
}
}
} else {
baseEach(collection, baseCreateCallback(callback, thisArg, 3));
}
return collection;
}
/**
* This method is like `_.forEach` except that it iterates over elements of
* a collection from right to left.
*
* @static
* @memberOf _
* @alias eachRight
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|string} Returns `collection`.
* @example
*
* _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
* // => logs each number from right to left and returns '3,2,1'
*/
function forEachRight(collection, callback, thisArg) {
if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
var length = collection.length;
while (length--) {
if (callback(collection[length], length, collection) === false) {
break;
}
}
} else {
baseEachRight(collection, baseCreateCallback(callback, thisArg, 3));
}
return collection;
}
/**
* Creates an object composed of keys generated from the results of running
* each element of a collection through the callback. The corresponding value
* of each key is an array of the elements responsible for generating the key.
* The callback is bound to `thisArg` and invoked with three arguments;
* (value, index|key, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
*
* _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
* // => { '4': [4.2], '6': [6.1, 6.4] }
*
* _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
* // => { '4': [4.2], '6': [6.1, 6.4] }
*
* // using "_.pluck" callback shorthand
* _.groupBy(['one', 'two', 'three'], 'length');
* // => { '3': ['one', 'two'], '5': ['three'] }
*/
var groupBy = createAggregator(function(result, value, key) {
if (hasOwnProperty.call(result, key)) {
result[key].push(value);
} else {
result[key] = [value];
}
});
/**
* Creates an object composed of keys generated from the results of running
* each element of the collection through the given callback. The corresponding
* value of each key is the last element responsible for generating the key.
* The callback is bound to `thisArg` and invoked with three arguments;
* (value, index|key, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
*
* var keyData = [
* { 'dir': 'left', 'code': 97 },
* { 'dir': 'right', 'code': 100 }
* ];
*
* _.indexBy(keyData, 'dir');
* // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
*
* _.indexBy(keyData, function(object) { return String.fromCharCode(object.code); });
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
*
* _.indexBy(keyData, function(object) { return this.fromCharCode(object.code); }, String);
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
*/
var indexBy = createAggregator(function(result, value, key) {
result[key] = value;
});
/**
* Invokes the method named by `methodName` on each element in the collection
* returning an array of the results of each invoked method. Additional arguments
* will be provided to each invoked method. If `methodName` is a function it
* will be invoked for, and `this` bound to, each element in the collection.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|string} methodName The name of the method to invoke or
* the function invoked per iteration.
* @param {...*} [args] Arguments to invoke the method with.
* @returns {Array} Returns the array of results.
* @example
*
* _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
* // => [[1, 5, 7], [1, 2, 3]]
*
* _.invoke([123, 456], String.prototype.split, '');
* // => [['1', '2', '3'], ['4', '5', '6']]
*/
function invoke(collection, methodName) {
var args = slice(arguments, 2),
index = -1,
isFunc = typeof methodName == 'function',
length = collection && collection.length,
result = Array(length < 0 ? 0 : length >>> 0);
baseEach(collection, function(value) {
var func = isFunc ? methodName : (value != null && value[methodName]);
result[++index] = func ? func.apply(value, args) : undefined;
});
return result;
}
/**
* Creates an array of values by running each element in the collection
* through the callback. The callback is bound to `thisArg` and invoked with
* three arguments; (value, index|key, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @alias collect
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns the new mapped array.
* @example
*
* _.map([1, 2, 3], function(num) { return num * 3; });
* // => [3, 6, 9]
*
* _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
* // => [3, 6, 9] (property order is not guaranteed across environments)
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* // using "_.pluck" callback shorthand
* _.map(characters, 'name');
* // => ['barney', 'fred']
*/
function map(collection, callback, thisArg) {
var index = -1,
length = collection && collection.length,
result = Array(length < 0 ? 0 : length >>> 0);
callback = lodash.createCallback(callback, thisArg, 3);
if (isArray(collection)) {
while (++index < length) {
result[index] = callback(collection[index], index, collection);
}
} else {
baseEach(collection, function(value, key, collection) {
result[++index] = callback(value, key, collection);
});
}
return result;
}
/**
* Retrieves the maximum value of a collection. If the collection is empty or
* falsey `-Infinity` is returned. If a callback is provided it will be executed
* for each value in the collection to generate the criterion by which the value
* is ranked. The callback is bound to `thisArg` and invoked with three
* arguments; (value, index, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback] The function called per iteration.
* If a property name or object is provided it will be used to create a "_.pluck"
* or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the maximum value.
* @example
*
* _.max([4, 2, 8, 6]);
* // => 8
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* _.max(characters, function(chr) { return chr.age; });
* // => { 'name': 'fred', 'age': 40 };
*
* // using "_.pluck" callback shorthand
* _.max(characters, 'age');
* // => { 'name': 'fred', 'age': 40 };
*/
function max(collection, callback, thisArg) {
var computed = -Infinity,
result = computed,
type = typeof callback;
// enables use as a callback for functions like `_.map`
if ((type == 'number' || type == 'string') && thisArg && thisArg[callback] === collection) {
callback = null;
}
if (callback == null && isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
if (value > result) {
result = value;
}
}
} else {
callback = (callback == null && isString(collection))
? charAtCallback
: lodash.createCallback(callback, thisArg, 3);
baseEach(collection, function(value, index, collection) {
var current = callback(value, index, collection);
if (current > computed) {
computed = current;
result = value;
}
});
}
return result;
}
/**
* Retrieves the minimum value of a collection. If the collection is empty or
* falsey `Infinity` is returned. If a callback is provided it will be executed
* for each value in the collection to generate the criterion by which the value
* is ranked. The callback is bound to `thisArg` and invoked with three
* arguments; (value, index, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback] The function called per iteration.
* If a property name or object is provided it will be used to create a "_.pluck"
* or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the minimum value.
* @example
*
* _.min([4, 2, 8, 6]);
* // => 2
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* _.min(characters, function(chr) { return chr.age; });
* // => { 'name': 'barney', 'age': 36 };
*
* // using "_.pluck" callback shorthand
* _.min(characters, 'age');
* // => { 'name': 'barney', 'age': 36 };
*/
function min(collection, callback, thisArg) {
var computed = Infinity,
result = computed,
type = typeof callback;
// enables use as a callback for functions like `_.map`
if ((type == 'number' || type == 'string') && thisArg && thisArg[callback] === collection) {
callback = null;
}
if (callback == null && isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
if (value < result) {
result = value;
}
}
} else {
callback = (callback == null && isString(collection))
? charAtCallback
: lodash.createCallback(callback, thisArg, 3);
baseEach(collection, function(value, index, collection) {
var current = callback(value, index, collection);
if (current < computed) {
computed = current;
result = value;
}
});
}
return result;
}
/**
* Creates an array of elements split into two groups, the first of which
* contains elements the predicate returns truthy for, while the second of which
* contains elements the predicate returns falsey for. The predicate is bound
* to `thisArg` and invoked with three arguments; (value, index|key, collection).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {Array} Returns the array of grouped elements.
* @example
*
* _.partition([1, 2, 3], function(num) { return num % 2; });
* // => [[1, 3], [2]]
*
* _.partition([1.2, 2.3, 3.4], function(num) { return this.floor(num) % 2; }, Math);
* // => [[1, 3], [2]]
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40, 'blocked': true },
* { 'name': 'pebbles', 'age': 1 }
* ];
*
* // using "_.where" callback shorthand
* _.map(_.partition(characters, { 'age': 1 }), function(array) { return _.pluck(array, 'name'); });
* // => [['pebbles'], ['barney', 'fred']]
*
* // using "_.pluck" callback shorthand
* _.map(_.partition(characters, 'blocked'), function(array) { return _.pluck(array, 'name'); });
* // => [['fred'], ['barney', 'pebbles']]
*/
var partition = createAggregator(function(result, value, key) {
result[key ? 0 : 1].push(value);
}, true);
/**
* Retrieves the value of a specified property from all elements in the collection.
*
* @static
* @memberOf _
* @type Function
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {string} key The name of the property to pluck.
* @returns {Array} Returns the property values.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* _.pluck(characters, 'name');
* // => ['barney', 'fred']
*/
var pluck = map;
/**
* Reduces a collection to a value which is the accumulated result of running
* each element in the collection through the callback, where each successive
* callback execution consumes the return value of the previous execution. If
* `accumulator` is not provided the first element of the collection will be
* used as the initial `accumulator` value. The callback is bound to `thisArg`
* and invoked with four arguments; (accumulator, value, index|key, collection).
*
* @static
* @memberOf _
* @alias foldl, inject
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [accumulator] Initial value of the accumulator.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the accumulated value.
* @example
*
* var sum = _.reduce([1, 2, 3], function(sum, num) {
* return sum + num;
* });
* // => 6
*
* var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
* result[key] = num * 3;
* return result;
* }, {});
* // => { 'a': 3, 'b': 6, 'c': 9 }
*/
function reduce(collection, callback, accumulator, thisArg) {
var noaccum = arguments.length < 3;
callback = lodash.createCallback(callback, thisArg, 4);
if (isArray(collection)) {
var index = -1,
length = collection.length;
if (noaccum && length) {
accumulator = collection[++index];
}
while (++index < length) {
accumulator = callback(accumulator, collection[index], index, collection);
}
} else {
baseEach(collection, function(value, index, collection) {
accumulator = noaccum
? (noaccum = false, value)
: callback(accumulator, value, index, collection)
});
}
return accumulator;
}
/**
* This method is like `_.reduce` except that it iterates over elements of a
* collection from right to left.
*
* @static
* @memberOf _
* @alias foldr
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [accumulator] Initial value of the accumulator.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the accumulated value.
* @example
*
* var array = [[0, 1], [2, 3], [4, 5]];
* _.reduceRight(array, function(flattened, other) { return flattened.concat(other); }, []);
* // => [4, 5, 2, 3, 0, 1]
*/
function reduceRight(collection, callback, accumulator, thisArg) {
var noaccum = arguments.length < 3;
callback = lodash.createCallback(callback, thisArg, 4);
baseEachRight(collection, function(value, index, collection) {
accumulator = noaccum
? (noaccum = false, value)
: callback(accumulator, value, index, collection);
});
return accumulator;
}
/**
* The opposite of `_.filter`; this method returns the elements of a collection
* the predicate does **not** return truthy for.
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {Array} Returns the new filtered array.
* @example
*
* var odds = _.reject([1, 2, 3, 4], function(num) { return num % 2 == 0; });
* // => [1, 3]
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40, 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.reject(characters, 'blocked');
* // => [{ 'name': 'barney', 'age': 36 }]
*
* // using "_.where" callback shorthand
* _.reject(characters, { 'age': 36 });
* // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
*/
function reject(collection, predicate, thisArg) {
predicate = lodash.createCallback(predicate, thisArg, 3);
return filter(collection, negate(predicate));
}
/**
* Retrieves a random element or `n` random elements from a collection.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to sample.
* @param {number} [n] The number of elements to sample.
* @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
* @returns {*} Returns the random sample(s).
* @example
*
* _.sample([1, 2, 3, 4]);
* // => 2
*
* _.sample([1, 2, 3, 4], 2);
* // => [3, 1]
*/
function sample(collection, n, guard) {
if (collection && typeof collection.length != 'number') {
collection = values(collection);
} else if (support.unindexedChars && isString(collection)) {
collection = collection.split('');
}
if (n == null || guard) {
var length = collection ? collection.length : 0;
return length > 0 ? collection[baseRandom(0, length - 1)] : undefined;
}
var result = shuffle(collection);
result.length = nativeMin(n < 0 ? 0 : (+n || 0), result.length);
return result;
}
/**
* Creates an array of shuffled values, using a version of the Fisher-Yates
* shuffle. See [Wikipedia](http://en.wikipedia.org/wiki/Fisher-Yates_shuffle)
* for more details.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to shuffle.
* @returns {Array} Returns the new shuffled array.
* @example
*
* _.shuffle([1, 2, 3, 4]);
* // => [4, 1, 3, 2]
*/
function shuffle(collection) {
var index = -1,
length = collection && collection.length,
result = Array(length < 0 ? 0 : length >>> 0);
baseEach(collection, function(value) {
var rand = baseRandom(0, ++index);
result[index] = result[rand];
result[rand] = value;
});
return result;
}
/**
* Gets the size of the collection by returning `collection.length` for arrays
* and array-like objects or the number of own enumerable properties for objects.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to inspect.
* @returns {number} Returns `collection.length` or number of own enumerable properties.
* @example
*
* _.size([1, 2]);
* // => 2
*
* _.size({ 'one': 1, 'two': 2, 'three': 3 });
* // => 3
*
* _.size('pebbles');
* // => 7
*/
function size(collection) {
var length = collection ? collection.length : 0;
return (typeof length == 'number' && length > -1 && length <= maxSafeInteger)
? length
: keys(collection).length;
}
/**
* Checks if the predicate returns truthy for **any** element of a collection.
* The function returns as soon as it finds a passing value and does not iterate
* over the entire collection. The predicate is bound to `thisArg` and invoked
* with three arguments; (value, index|key, collection).
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @alias any
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {boolean} Returns `true` if any element passed the predicate check,
* else `false`.
* @example
*
* _.some([null, 0, 'yes', false], Boolean);
* // => true
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40, 'blocked': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.some(characters, 'blocked');
* // => true
*
* // using "_.where" callback shorthand
* _.some(characters, { 'age': 1 });
* // => false
*/
function some(collection, predicate, thisArg) {
var result;
predicate = lodash.createCallback(predicate, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
if (predicate(collection[index], index, collection)) {
return true;
}
}
} else {
baseEach(collection, function(value, index, collection) {
return !(result = predicate(value, index, collection));
});
}
return !!result;
}
/**
* Creates an array of elements, sorted in ascending order by the results of
* running each element in a collection through the callback. This method
* performs a stable sort, that is, it will preserve the original sort order
* of equal elements. The callback is bound to `thisArg` and invoked with
* three arguments; (value, index|key, collection).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an array of property names is provided for `callback` the collection
* will be sorted by each property value.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Array|Function|Object|string} [callback=identity] The function
* called per iteration. If a property name or object is provided it will
* be used to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns the new sorted array.
* @example
*
* _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
* // => [3, 1, 2]
*
* _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
* // => [3, 1, 2]
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 },
* { 'name': 'barney', 'age': 26 },
* { 'name': 'fred', 'age': 30 }
* ];
*
* // using "_.pluck" callback shorthand
* _.map(_.sortBy(characters, 'age'), _.values);
* // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
*
* // sorting by multiple properties
* _.map(_.sortBy(characters, ['name', 'age']), _.values);
* // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
*/
function sortBy(collection, callback, thisArg) {
var index = -1,
length = collection && collection.length,
multi = callback && isArray(callback),
result = Array(length < 0 ? 0 : length >>> 0);
if (!multi) {
callback = lodash.createCallback(callback, thisArg, 3);
}
baseEach(collection, function(value, key, collection) {
if (multi) {
var length = callback.length,
criteria = Array(length);
while (length--) {
criteria[length] = value[callback[length]];
}
} else {
criteria = callback(value, key, collection);
}
result[++index] = { 'criteria': criteria, 'index': index, 'value': value };
});
length = result.length;
result.sort(multi ? compareMultipleAscending : compareAscending);
while (length--) {
result[length] = result[length].value;
}
return result;
}
/**
* Converts `collection` to an array.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to convert.
* @returns {Array} Returns the new converted array.
* @example
*
* (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
* // => [2, 3, 4]
*/
function toArray(collection) {
var length = collection && collection.length;
if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
return (support.unindexedChars && isString(collection))
? collection.split('')
: slice(collection);
}
return values(collection);
}
/**
* Performs a deep comparison between each element in `collection` and the
* source object, returning an array of all elements that have equivalent
* property values.
*
* @static
* @memberOf _
* @type Function
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Object} source The object of property values to filter by.
* @returns {Array} Returns the new filtered array.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
* { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
* ];
*
* _.where(characters, { 'age': 36 });
* // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
*
* _.where(characters, { 'pets': ['dino'] });
* // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
*/
var where = filter;
/*--------------------------------------------------------------------------*/
/**
* Creates a function that executes `func`, with the `this` binding and
* arguments of the created function, only after being called `n` times.
*
* @static
* @memberOf _
* @category Functions
* @param {number} n The number of times the function must be called before
* `func` is executed.
* @param {Function} func The function to restrict.
* @returns {Function} Returns the new restricted function.
* @example
*
* var saves = ['profile', 'settings'];
*
* var done = _.after(saves.length, function() {
* console.log('Done saving!');
* });
*
* _.forEach(saves, function(type) {
* asyncSave({ 'type': type, 'complete': done });
* });
* // => logs 'Done saving!', after all saves have completed
*/
function after(n, func) {
if (!isFunction(func)) {
throw new TypeError;
}
n = nativeIsFinite(n = +n) ? n : 0;
return function() {
if (--n < 1) {
return func.apply(this, arguments);
}
};
}
/**
* Creates a function that invokes `func` with the `this` binding of `thisArg`
* and prepends any additional `bind` arguments to those provided to the bound
* function.
*
* Note: Unlike native `Function#bind` this method does not set the `length`
* property of bound functions.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to bind.
* @param {*} [thisArg] The `this` binding of `func`.
* @param {...*} [args] Arguments to be partially applied.
* @returns {Function} Returns the new bound function.
* @example
*
* var func = function(greeting) {
* return greeting + ' ' + this.name;
* };
*
* func = _.bind(func, { 'name': 'fred' }, 'hi');
* func();
* // => 'hi fred'
*/
function bind(func, thisArg) {
if (arguments.length < 3) {
return createWrapper(func, BIND_FLAG, null, thisArg);
}
if (func) {
var arity = func[expando] ? func[expando][2] : func.length,
partialArgs = slice(arguments, 2);
arity -= partialArgs.length;
}
return createWrapper(func, BIND_FLAG | PARTIAL_FLAG, arity, thisArg, partialArgs);
}
/**
* Binds methods of an object to the object itself, overwriting the existing
* method. Method names may be specified as individual arguments or as arrays
* of method names. If no method names are provided all the function properties
* of `object` will be bound.
*
* Note: This method does not set the `length` property of bound functions.
*
* @static
* @memberOf _
* @category Functions
* @param {Object} object The object to bind and assign the bound methods to.
* @param {...string} [methodName] The object method names to
* bind, specified as individual method names or arrays of method names.
* @returns {Object} Returns `object`.
* @example
*
* var view = {
* 'label': 'docs',
* 'onClick': function() { console.log('clicked ' + this.label); }
* };
*
* _.bindAll(view);
* jQuery('#docs').on('click', view.onClick);
* // => logs 'clicked docs', when the button is clicked
*/
function bindAll(object) {
var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
index = -1,
length = funcs.length;
while (++index < length) {
var key = funcs[index];
object[key] = createWrapper(object[key], BIND_FLAG, null, object);
}
return object;
}
/**
* Creates a function that invokes the method at `object[key]` and prepends
* any additional `bindKey` arguments to those provided to the bound function.
* This method differs from `_.bind` by allowing bound functions to reference
* methods that will be redefined or don't yet exist.
* See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern)
* for more details.
*
* @static
* @memberOf _
* @category Functions
* @param {Object} object The object the method belongs to.
* @param {string} key The key of the method.
* @param {...*} [args] Arguments to be partially applied.
* @returns {Function} Returns the new bound function.
* @example
*
* var object = {
* 'name': 'fred',
* 'greet': function(greeting) {
* return greeting + ' ' + this.name;
* }
* };
*
* var func = _.bindKey(object, 'greet', 'hi');
* func();
* // => 'hi fred'
*
* object.greet = function(greeting) {
* return greeting + 'ya ' + this.name + '!';
* };
*
* func();
* // => 'hiya fred!'
*/
function bindKey(object, key) {
return arguments.length < 3
? createWrapper(key, BIND_FLAG | BIND_KEY_FLAG, null, object)
: createWrapper(key, BIND_FLAG | BIND_KEY_FLAG | PARTIAL_FLAG, null, object, slice(arguments, 2));
}
/**
* Creates a function that is the composition of the provided functions,
* where each function consumes the return value of the function that follows.
* For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
* Each function is executed with the `this` binding of the composed function.
*
* @static
* @memberOf _
* @category Functions
* @param {...Function} [funcs] Functions to compose.
* @returns {Function} Returns the new composed function.
* @example
*
* var realNameMap = {
* 'pebbles': 'penelope'
* };
*
* var format = function(name) {
* name = realNameMap[name.toLowerCase()] || name;
* return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
* };
*
* var greet = function(formatted) {
* return 'Hiya ' + formatted + '!';
* };
*
* var welcome = _.compose(greet, format);
* welcome('pebbles');
* // => 'Hiya Penelope!'
*/
function compose() {
var funcs = arguments,
funcsLength = funcs.length,
length = funcsLength;
while (length--) {
if (!isFunction(funcs[length])) {
throw new TypeError;
}
}
return function() {
var length = funcsLength - 1,
result = funcs[length].apply(this, arguments);
while (length--) {
result = funcs[length].call(this, result);
}
return result;
};
}
/**
* Creates a function which accepts one or more arguments of `func` that when
* invoked either executes `func` returning its result, if all `func` arguments
* have been provided, or returns a function that accepts one or more of the
* remaining `func` arguments, and so on. The arity of `func` can be specified
* if `func.length` is not sufficient.
*
* Note: This method does not set the `length` property of curried functions.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to curry.
* @param {number} [arity=func.length] The arity of `func`.
* @returns {Function} Returns the new curried function.
* @example
*
* var curried = _.curry(function(a, b, c) {
* console.log(a + b + c);
* });
*
* curried(1)(2)(3);
* // => 6
*
* curried(1, 2)(3);
* // => 6
*
* curried(1, 2, 3);
* // => 6
*/
function curry(func, arity) {
if (typeof arity != 'number') {
arity = +arity || (func ? func.length : 0);
}
return createWrapper(func, CURRY_FLAG, arity);
}
/**
* Creates a function that will delay the execution of `func` until after
* `wait` milliseconds have elapsed since the last time it was invoked.
* Provide an options object to indicate that `func` should be invoked on
* the leading and/or trailing edge of the `wait` timeout. Subsequent calls
* to the debounced function will return the result of the last `func` call.
*
* Note: If `leading` and `trailing` options are `true`, `func` will be called
* on the trailing edge of the timeout only if the the debounced function is
* invoked more than once during the `wait` timeout.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to debounce.
* @param {number} wait The number of milliseconds to delay.
* @param {Object} [options] The options object.
* @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
* @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
* @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // avoid costly calculations while the window size is in flux
* var lazyLayout = _.debounce(calculateLayout, 150);
* jQuery(window).on('resize', lazyLayout);
*
* // execute `sendMail` when the click event is fired, debouncing subsequent calls
* jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* });
*
* // ensure `batchLog` is executed once after 1 second of debounced calls
* var source = new EventSource('/stream');
* source.addEventListener('message', _.debounce(batchLog, 250, {
* 'maxWait': 1000
* }, false);
*/
function debounce(func, wait, options) {
var args,
maxTimeoutId,
result,
stamp,
thisArg,
timeoutId,
trailingCall,
lastCalled = 0,
maxWait = false,
trailing = true;
if (!isFunction(func)) {
throw new TypeError;
}
wait = wait < 0 ? 0 : wait;
if (options === true) {
var leading = true;
trailing = false;
} else if (isObject(options)) {
leading = options.leading;
maxWait = 'maxWait' in options && nativeMax(wait, +options.maxWait || 0);
trailing = 'trailing' in options ? options.trailing : trailing;
}
var delayed = function() {
var remaining = wait - (now() - stamp);
if (remaining <= 0 || remaining > wait) {
if (maxTimeoutId) {
clearTimeout(maxTimeoutId);
}
var isCalled = trailingCall;
maxTimeoutId = timeoutId = trailingCall = undefined;
if (isCalled) {
lastCalled = now();
result = func.apply(thisArg, args);
if (!timeoutId && !maxTimeoutId) {
args = thisArg = null;
}
}
} else {
timeoutId = setTimeout(delayed, remaining);
}
};
var maxDelayed = function() {
if (timeoutId) {
clearTimeout(timeoutId);
}
maxTimeoutId = timeoutId = trailingCall = undefined;
if (trailing || (maxWait !== wait)) {
lastCalled = now();
result = func.apply(thisArg, args);
if (!timeoutId && !maxTimeoutId) {
args = thisArg = null;
}
}
};
return function() {
args = arguments;
stamp = now();
thisArg = this;
trailingCall = trailing && (timeoutId || !leading);
if (maxWait === false) {
var leadingCall = leading && !timeoutId;
} else {
if (!maxTimeoutId && !leading) {
lastCalled = stamp;
}
var remaining = maxWait - (stamp - lastCalled),
isCalled = remaining <= 0 || remaining > maxWait;
if (isCalled) {
if (maxTimeoutId) {
maxTimeoutId = clearTimeout(maxTimeoutId);
}
lastCalled = stamp;
result = func.apply(thisArg, args);
}
else if (!maxTimeoutId) {
maxTimeoutId = setTimeout(maxDelayed, remaining);
}
}
if (isCalled && timeoutId) {
timeoutId = clearTimeout(timeoutId);
}
else if (!timeoutId && wait !== maxWait) {
timeoutId = setTimeout(delayed, wait);
}
if (leadingCall) {
isCalled = true;
result = func.apply(thisArg, args);
}
if (isCalled && !timeoutId && !maxTimeoutId) {
args = thisArg = null;
}
return result;
};
}
/**
* Defers executing the `func` function until the current call stack has cleared.
* Additional arguments will be provided to `func` when it is invoked.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to defer.
* @param {...*} [args] Arguments to invoke the function with.
* @returns {number} Returns the timer id.
* @example
*
* _.defer(function(text) { console.log(text); }, 'deferred');
* // logs 'deferred' after one or more milliseconds
*/
function defer(func) {
if (!isFunction(func)) {
throw new TypeError;
}
var args = slice(arguments, 1);
return setTimeout(function() { func.apply(undefined, args); }, 1);
}
/**
* Executes the `func` function after `wait` milliseconds. Additional arguments
* will be provided to `func` when it is invoked.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to delay.
* @param {number} wait The number of milliseconds to delay execution.
* @param {...*} [args] Arguments to invoke the function with.
* @returns {number} Returns the timer id.
* @example
*
* _.delay(function(text) { console.log(text); }, 1000, 'later');
* // => logs 'later' after one second
*/
function delay(func, wait) {
if (!isFunction(func)) {
throw new TypeError;
}
var args = slice(arguments, 2);
return setTimeout(function() { func.apply(undefined, args); }, wait);
}
/**
* Creates a function that memoizes the result of `func`. If `resolver` is
* provided it will be used to determine the cache key for storing the result
* based on the arguments provided to the memoized function. By default, the
* first argument provided to the memoized function is used as the cache key.
* The `func` is executed with the `this` binding of the memoized function.
* The result cache is exposed as the `cache` property on the memoized function.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to have its output memoized.
* @param {Function} [resolver] The function to resolve the cache key.
* @returns {Function} Returns the new memoizing function.
* @example
*
* var fibonacci = _.memoize(function(n) {
* return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
* });
*
* fibonacci(9)
* // => 34
*
* var data = {
* 'fred': { 'name': 'fred', 'age': 40 },
* 'pebbles': { 'name': 'pebbles', 'age': 1 }
* };
*
* // modifying the result cache
* var get = _.memoize(function(name) { return data[name]; }, _.identity);
* get('pebbles');
* // => { 'name': 'pebbles', 'age': 1 }
*
* get.cache.pebbles.name = 'penelope';
* get('pebbles');
* // => { 'name': 'penelope', 'age': 1 }
*/
function memoize(func, resolver) {
if (!isFunction(func)) {
throw new TypeError;
}
var memoized = function() {
var cache = memoized.cache,
key = resolver ? resolver.apply(this, arguments) : '_' + arguments[0];
return hasOwnProperty.call(cache, key)
? cache[key]
: (cache[key] = func.apply(this, arguments));
}
memoized.cache = {};
return memoized;
}
/**
* Creates a function that negates the result of the predicate `func`. The
* `func` function is executed with the `this` binding and arguments of the
* created function.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} predicate The predicate to negate.
* @returns {Function} Returns the new function.
* @example
*
* function isEven(num) {
* return num % 2 == 0;
* }
*
* _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
* // => [1, 3, 5]
*/
function negate(predicate) {
if (!isFunction(predicate)) {
throw new TypeError;
}
return function() {
return !predicate.apply(this, arguments);
};
}
/**
* Creates a function that is restricted to execute `func` once. Repeat calls to
* the function will return the value of the first call. The `func` is executed
* with the `this` binding of the created function.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to restrict.
* @returns {Function} Returns the new restricted function.
* @example
*
* var initialize = _.once(createApplication);
* initialize();
* initialize();
* // `initialize` executes `createApplication` once
*/
function once(func) {
var ran,
result;
if (!isFunction(func)) {
throw new TypeError;
}
return function() {
if (ran) {
return result;
}
ran = true;
result = func.apply(this, arguments);
// clear the `func` variable so the function may be garbage collected
func = null;
return result;
};
}
/**
* Creates a function that invokes `func` with any additional `partial` arguments
* prepended to those provided to the new function. This method is similar to
* `_.bind` except it does **not** alter the `this` binding.
*
* Note: This method does not set the `length` property of partially applied
* functions.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to partially apply arguments to.
* @param {...*} [args] Arguments to be partially applied.
* @returns {Function} Returns the new partially applied function.
* @example
*
* var greet = function(greeting, name) { return greeting + ' ' + name; };
* var hi = _.partial(greet, 'hi');
* hi('fred');
* // => 'hi fred'
*/
function partial(func) {
if (func) {
var arity = func[expando] ? func[expando][2] : func.length,
partialArgs = slice(arguments, 1);
arity -= partialArgs.length;
}
return createWrapper(func, PARTIAL_FLAG, arity, null, partialArgs);
}
/**
* This method is like `_.partial` except that partially applied arguments
* are appended to those provided to the new function.
*
* Note: This method does not set the `length` property of partially applied
* functions.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to partially apply arguments to.
* @param {...*} [args] Arguments to be partially applied.
* @returns {Function} Returns the new partially applied function.
* @example
*
* var defaultsDeep = _.partialRight(_.merge, _.defaults);
*
* var options = {
* 'variable': 'data',
* 'imports': { 'jq': $ }
* };
*
* defaultsDeep(options, _.templateSettings);
*
* options.variable
* // => 'data'
*
* options.imports
* // => { '_': _, 'jq': $ }
*/
function partialRight(func) {
if (func) {
var arity = func[expando] ? func[expando][2] : func.length,
partialRightArgs = slice(arguments, 1);
arity -= partialRightArgs.length;
}
return createWrapper(func, PARTIAL_RIGHT_FLAG, arity, null, null, partialRightArgs);
}
/**
* Creates a function that, when executed, will only call the `func` function
* at most once per every `wait` milliseconds. Provide an options object to
* indicate that `func` should be invoked on the leading and/or trailing edge
* of the `wait` timeout. Subsequent calls to the throttled function will
* return the result of the last `func` call.
*
* Note: If `leading` and `trailing` options are `true`, `func` will be called
* on the trailing edge of the timeout only if the the throttled function is
* invoked more than once during the `wait` timeout.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to throttle.
* @param {number} wait The number of milliseconds to throttle executions to.
* @param {Object} [options] The options object.
* @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
* @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
* @returns {Function} Returns the new throttled function.
* @example
*
* // avoid excessively updating the position while scrolling
* var throttled = _.throttle(updatePosition, 100);
* jQuery(window).on('scroll', throttled);
*
* // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
* jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
* 'trailing': false
* }));
*/
function throttle(func, wait, options) {
var leading = true,
trailing = true;
if (!isFunction(func)) {
throw new TypeError;
}
if (options === false) {
leading = false;
} else if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading;
trailing = 'trailing' in options ? !!options.trailing : trailing;
}
debounceOptions.leading = leading;
debounceOptions.maxWait = +wait;
debounceOptions.trailing = trailing;
return debounce(func, wait, debounceOptions);
}
/**
* Creates a function that provides `value` to the wrapper function as its
* first argument. Additional arguments provided to the function are appended
* to those provided to the wrapper function. The wrapper is executed with
* the `this` binding of the created function.
*
* @static
* @memberOf _
* @category Functions
* @param {*} value The value to wrap.
* @param {Function} wrapper The wrapper function.
* @returns {Function} Returns the new function.
* @example
*
* var p = _.wrap(_.escape, function(func, text) {
* return '<p>' + func(text) + '</p>';
* });
*
* p('fred, barney, & pebbles');
* // => '<p>fred, barney, & pebbles</p>'
*/
function wrap(value, wrapper) {
return createWrapper(wrapper, PARTIAL_FLAG, null, null, [value]);
}
/*--------------------------------------------------------------------------*/
/**
* Assigns own enumerable properties of source object(s) to the destination
* object. Subsequent sources will overwrite property assignments of previous
* sources. If a callback is provided it will be executed to produce the
* assigned values. The callback is bound to `thisArg` and invoked with two
* arguments; (objectValue, sourceValue).
*
* @static
* @memberOf _
* @alias extend
* @category Objects
* @param {Object} object The destination object.
* @param {...Object} [source] The source objects.
* @param {Function} [callback] The function to customize assigning values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the destination object.
* @example
*
* _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
* // => { 'name': 'fred', 'employer': 'slate' }
*
* var defaults = _.partialRight(_.assign, function(value, other) {
* return typeof value == 'undefined' ? other : value;
* });
*
* defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' });
* // => { 'name': 'barney', 'employer': 'slate' }
*/
function assign(object, source, guard) {
var args = arguments;
Iif (!object || args.length < 2) {
return object;
}
var argsIndex = 0,
argsLength = args.length,
type = typeof guard;
// enables use as a callback for functions like `_.reduce`
Iif ((type == 'number' || type == 'string') && args[3] && args[3][guard] === source) {
argsLength = 2;
}
// juggle arguments
Iif (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
} else Iif (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
callback = args[--argsLength];
}
while (++argsIndex < argsLength) {
source = args[argsIndex];
var index = -1,
props = keys(source),
length = props.length;
while (++index < length) {
var key = props[index];
object[key] = callback ? callback(object[key], source[key]) : source[key];
}
}
return object;
}
/**
* Creates a clone of `value`. If `isDeep` is `true` nested objects will also
* be cloned, otherwise they will be assigned by reference. If a callback
* is provided it will be executed to produce the cloned values. If the
* callback returns `undefined` cloning will be handled by the method instead.
* The callback is bound to `thisArg` and invoked with one argument; (value).
*
* Note: This method is loosely based on the structured clone algorithm. Functions
* and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
* objects created by constructors other than `Object` are cloned to plain `Object` objects.
* See the [HTML5 specification](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm)
* for more details.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to clone.
* @param {boolean} [isDeep=false] Specify a deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the cloned value.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* var shallow = _.clone(characters);
* shallow[0] === characters[0];
* // => true
*
* var deep = _.clone(characters, true);
* deep[0] === characters[0];
* // => false
*
* _.mixin({
* 'clone': _.partialRight(_.clone, function(value) {
* return _.isElement(value) ? value.cloneNode(false) : undefined;
* })
* });
*
* var clone = _.clone(document.body);
* clone.childNodes.length;
* // => 0
*/
function clone(value, isDeep, callback, thisArg) {
var type = typeof isDeep;
// juggle arguments
if (type != 'boolean' && isDeep != null) {
thisArg = callback;
callback = isDeep;
isDeep = false;
// enables use as a callback for functions like `_.map`
if ((type == 'number' || type == 'string') && thisArg && thisArg[callback] === value) {
callback = null;
}
}
callback = typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1);
return baseClone(value, isDeep, callback);
}
/**
* Creates a deep clone of `value`. If a callback is provided it will be
* executed to produce the cloned values. If the callback returns `undefined`
* cloning will be handled by the method instead. The callback is bound to
* `thisArg` and invoked with one argument; (value).
*
* Note: This method is loosely based on the structured clone algorithm. Functions
* and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
* objects created by constructors other than `Object` are cloned to plain `Object` objects.
* See the [HTML5 specification](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm)
* for more details.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the deep cloned value.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* var deep = _.cloneDeep(characters);
* deep[0] === characters[0];
* // => false
*
* var view = {
* 'label': 'docs',
* 'node': element
* };
*
* var clone = _.cloneDeep(view, function(value) {
* return _.isElement(value) ? value.cloneNode(true) : undefined;
* });
*
* clone.node == view.node;
* // => false
*/
function cloneDeep(value, callback, thisArg) {
callback = typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1);
return baseClone(value, true, callback);
}
/**
* Creates an object that inherits from the given `prototype` object. If a
* `properties` object is provided its own enumerable properties are assigned
* to the created object.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} prototype The object to inherit from.
* @param {Object} [properties] The properties to assign to the object.
* @returns {Object} Returns the new object.
* @example
*
* function Shape() {
* this.x = 0;
* this.y = 0;
* }
*
* function Circle() {
* Shape.call(this);
* }
*
* Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
*
* var circle = new Circle;
* circle instanceof Circle;
* // => true
*
* circle instanceof Shape;
* // => true
*/
function create(prototype, properties) {
var result = baseCreate(prototype);
return properties ? assign(result, properties) : result;
}
/**
* Assigns own enumerable properties of source object(s) to the destination
* object for all destination properties that resolve to `undefined`. Once a
* property is set, additional defaults of the same property will be ignored.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
* @param {...Object} [source] The source objects.
* @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
* @returns {Object} Returns the destination object.
* @example
*
* _.defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' });
* // => { 'name': 'barney', 'employer': 'slate' }
*/
function defaults(object) {
if (!object || arguments.length < 2) {
return object;
}
var args = slice(arguments);
args.push(assignDefaults);
return assign.apply(null, args);
}
/**
* This method is like `_.findIndex` except that it returns the key of the
* first element the predicate returns truthy for, instead of the element itself.
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to search.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {string|undefined} Returns the key of the found element, else `undefined`.
* @example
*
* var characters = {
* 'barney': { 'age': 36 },
* 'fred': { 'age': 40, 'blocked': true },
* 'pebbles': { 'age': 1 }
* };
*
* _.findKey(characters, function(chr) {
* return chr.age < 40;
* });
* // => 'barney' (property order is not guaranteed across environments)
*
* // using "_.where" callback shorthand
* _.findKey(characters, { 'age': 1 });
* // => 'pebbles'
*
* // using "_.pluck" callback shorthand
* _.findKey(characters, 'blocked');
* // => 'fred'
*/
function findKey(object, predicate, thisArg) {
predicate = lodash.createCallback(predicate, thisArg, 3);
return baseFind(object, predicate, baseForOwn, true);
}
/**
* This method is like `_.findKey` except that it iterates over elements of
* a collection in the opposite order.
*
* If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to search.
* @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {string|undefined} Returns the key of the found element, else `undefined`.
* @example
*
* var characters = {
* 'barney': { 'age': 36, 'blocked': true },
* 'fred': { 'age': 40 },
* 'pebbles': { 'age': 1, 'blocked': true }
* };
*
* _.findLastKey(characters, function(chr) {
* return chr.age < 40;
* });
* // => returns `pebbles`, assuming `_.findKey` returns `barney`
*
* // using "_.where" callback shorthand
* _.findLastKey(characters, { 'age': 40 });
* // => 'fred'
*
* // using "_.pluck" callback shorthand
* _.findLastKey(characters, 'blocked');
* // => 'pebbles'
*/
function findLastKey(object, predicate, thisArg) {
predicate = lodash.createCallback(predicate, thisArg, 3);
return baseFind(object, predicate, baseForOwnRight, true);
}
/**
* Iterates over own and inherited enumerable properties of an object executing
* the callback for each property. The callback is bound to `thisArg` and invoked
* with three arguments; (value, key, object). Callbacks may exit iteration
* early by explicitly returning `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* function Shape() {
* this.x = 0;
* this.y = 0;
* }
*
* Shape.prototype.z = 0;
*
* _.forIn(new Shape, function(value, key) {
* console.log(key);
* });
* // => logs 'x', 'y', and 'z' (property order is not guaranteed across environments)
*/
function forIn(object, callback, thisArg) {
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
return baseFor(object, callback, keysIn);
}
/**
* This method is like `_.forIn` except that it iterates over elements of a
* collection in the opposite order.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* function Shape() {
* this.x = 0;
* this.y = 0;
* }
*
* Shape.prototype.z = 0;
*
* _.forInRight(new Shape, function(value, key) {
* console.log(key);
* });
* // => logs 'z', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'z'
*/
function forInRight(object, callback, thisArg) {
callback = baseCreateCallback(callback, thisArg, 3);
return baseForRight(object, callback, keysIn);
}
/**
* Iterates over own enumerable properties of an object executing the callback
* for each property. The callback is bound to `thisArg` and invoked with three
* arguments; (value, key, object). Callbacks may exit iteration early by
* explicitly returning `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
* console.log(key);
* });
* // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
*/
function forOwn(object, callback, thisArg) {
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
return baseForOwn(object, callback);
}
/**
* This method is like `_.forOwn` except that it iterates over elements of a
* collection in the opposite order.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
* console.log(key);
* });
* // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
*/
function forOwnRight(object, callback, thisArg) {
callback = baseCreateCallback(callback, thisArg, 3);
return baseForRight(object, callback, keys);
}
/**
* Creates a sorted array of property names of all enumerable properties,
* own and inherited, of `object` that have function values.
*
* @static
* @memberOf _
* @alias methods
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns the new sorted array of property names.
* @example
*
* _.functions(_);
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
*/
function functions(object) {
var result = [];
baseForIn(object, function(value, key) {
if (isFunction(value)) {
result.push(key);
}
});
return result.sort();
}
/**
* Checks if the specified property name exists as a direct property of `object`,
* instead of an inherited property.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @param {string} key The name of the property to check.
* @returns {boolean} Returns `true` if key is a direct property, else `false`.
* @example
*
* _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
* // => true
*/
function has(object, key) {
return object ? hasOwnProperty.call(object, key) : false;
}
/**
* Creates an object composed of the inverted keys and values of the given
* object. If the given object contains duplicate values, subsequent values
* will overwrite property assignments of previous values unless `multiValue`
* is `true`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to invert.
* @param {boolean} [multiValue=false] Allow multiple values per key.
* @returns {Object} Returns the new inverted object.
* @example
*
* _.invert({ 'first': 'fred', 'second': 'barney' });
* // => { 'fred': 'first', 'barney': 'second' }
*
* // without `multiValue`
* _.invert({ 'first': 'fred', 'second': 'barney', 'third': 'fred' });
* // => { 'fred': 'third', 'barney': 'second' }
*
* // with `multiValue`
* _.invert({ 'first': 'fred', 'second': 'barney', 'third': 'fred' }, true);
* // => { 'fred': ['first', 'third'], 'barney': ['second'] }
*/
function invert(object, multiValue) {
var index = -1,
props = keys(object),
length = props.length,
result = {};
while (++index < length) {
var key = props[index],
value = object[key];
if (multiValue) {
if (hasOwnProperty.call(result, value)) {
result[value].push(key);
} else {
result[value] = [key];
}
}
else {
result[value] = key;
}
}
return result;
}
/**
* Checks if `value` is an `arguments` object.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`.
* @example
*
* (function() { return _.isArguments(arguments); })();
* // => true
*
* _.isArguments([1, 2, 3]);
* // => false
*/
function isArguments(value) {
return value && typeof value == 'object' && typeof value.length == 'number' &&
toString.call(value) == argsClass || false;
}
// fallback for environments that can't detect `arguments` objects by `[[Class]]`
Iif (!support.argsClass) {
isArguments = function(value) {
return value && typeof value == 'object' && typeof value.length == 'number' &&
hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false;
};
}
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
* _.isArray([1, 2, 3]);
* // => true
*
* (function() { return _.isArray(arguments); })();
* // => false
*/
var isArray = nativeIsArray || function(value) {
return value && typeof value == 'object' && typeof value.length == 'number' &&
toString.call(value) == arrayClass || false;
};
/**
* Checks if `value` is a boolean value.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a boolean value, else `false`.
* @example
*
* _.isBoolean(false);
* // => true
*
* _.isBoolean(null);
* // => false
*/
function isBoolean(value) {
return value === true || value === false ||
value && typeof value == 'object' && toString.call(value) == boolClass || false;
}
/**
* Checks if `value` is a date.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a date, else `false`.
* @example
*
* _.isDate(new Date);
* // => true
*
* _.isDate('Wed May 23 2012');
* // => false
*/
function isDate(value) {
return value && typeof value == 'object' && toString.call(value) == dateClass || false;
}
/**
* Checks if `value` is a DOM element.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
* @example
*
* _.isElement(document.body);
* // => true
*
* _.isElement('<body>');
* // => false
*/
function isElement(value) {
return value && typeof value == 'object' && value.nodeType === 1 &&
(support.nodeClass ? toString.call(value).indexOf('Element') > -1 : isNode(value)) || false;
}
// fallback for environments without DOM support
Eif (!support.dom) {
isElement = function(value) {
return value && typeof value == 'object' && value.nodeType === 1 &&
!isPlainObject(value) || false;
};
}
/**
* Checks if a collection is empty. A value is considered empty unless it is
* an array, array-like object, or string with a length greater than `0` or
* an object with own properties.
*
* @static
* @memberOf _
* @category Objects
* @param {Array|Object|string} value The value to inspect.
* @returns {boolean} Returns `true` if `value` is empty, else `false`.
* @example
*
* _.isEmpty(null);
* // => true
*
* _.isEmpty(true);
* // => true
*
* _.isEmpty(1);
* // => true
*
* _.isEmpty([1, 2, 3]);
* // => false
*
* _.isEmpty({ 'a': 1 });
* // => false
*/
function isEmpty(value) {
var result = true;
if (!value) {
return result;
}
var className = toString.call(value),
length = value.length;
if (length > -1 && length <= maxSafeInteger && (
(className == arrayClass || className == stringClass ||
(support.argsClass ? className == argsClass : isArguments(value))) ||
(className == objectClass && isFunction(value.splice))
)) {
return !length;
}
baseForOwn(value, function() {
return (result = false);
});
return result;
}
/**
* Performs a deep comparison between two values to determine if they are
* equivalent. If a callback is provided it will be executed to compare
* values. If the callback returns `undefined` comparisons will be handled
* by the method instead. The callback is bound to `thisArg` and invoked
* with two arguments; (value, other).
*
* Note: This method supports comparing arrays, booleans, `Date` objects,
* numbers, `Object` objects, regexes, and strings. Functions and DOM nodes
* are **not** supported. A callback may be used to extend support for
* comparing other values.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to compare to `other`.
* @param {*} other The value to compare to `value`.
* @param {Function} [callback] The function to customize comparing values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
* var object = { 'name': 'fred' };
* var other = { 'name': 'fred' };
*
* object == other;
* // => false
*
* _.isEqual(object, other);
* // => true
*
* var words = ['hello', 'goodbye'];
* var otherWords = ['hi', 'goodbye'];
*
* _.isEqual(words, otherWords, function() {
* return _.every(arguments, _.bind(RegExp.prototype.test, /^h(?:i|ello)$/)) || undefined;
* });
* // => true
*/
function isEqual(value, other, callback, thisArg) {
callback = typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2);
if (!callback) {
// exit early for identical values
if (value === other) {
// treat `-0` vs. `+0` as not equal
return value !== 0 || (1 / value == 1 / other);
}
var valType = typeof value,
othType = typeof other;
// exit early for unlike primitive values
if (value === value && (value == null || other == null ||
(valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) {
return false;
}
}
return baseIsEqual(value, other, callback);
}
/**
* Checks if `value` is, or can be coerced to, a finite number.
*
* Note: This method is not the same as native `isFinite` which will return
* `true` for booleans and empty strings. See the [ES5 spec](http://es5.github.io/#x15.1.2.5)
* for more details.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is finite, else `false`.
* @example
*
* _.isFinite(-101);
* // => true
*
* _.isFinite('10');
* // => true
*
* _.isFinite(true);
* // => false
*
* _.isFinite('');
* // => false
*
* _.isFinite(Infinity);
* // => false
*/
function isFinite(value) {
return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
}
/**
* Checks if `value` is a function.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a function, else `false`.
* @example
*
* _.isFunction(_);
* // => true
*
* _.isFunction(/abc/);
* // => false
*/
function isFunction(value) {
return typeof value == 'function';
}
// fallback for older versions of Chrome and Safari
Iif (isFunction(/x/)) {
isFunction = function(value) {
return typeof value == 'function' && toString.call(value) == funcClass;
};
}
/**
* Checks if `value` is the language type of `Object`.
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
* // => true
*
* _.isObject([1, 2, 3]);
* // => true
*
* _.isObject(1);
* // => false
*/
function isObject(value) {
// check if the value is the ECMAScript language type of `Object`
// http://es5.github.io/#x8
// and avoid a V8 bug
// https://code.google.com/p/v8/issues/detail?id=2291
var type = typeof value;
return value && (type == 'function' || type == 'object') || false;
}
/**
* Checks if `value` is `NaN`.
*
* Note: This method is not the same as native `isNaN` which will return `true`
* for `undefined` and other non-numeric values. See the [ES5 spec](http://es5.github.io/#x15.1.2.4)
* for more details.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
* @example
*
* _.isNaN(NaN);
* // => true
*
* _.isNaN(new Number(NaN));
* // => true
*
* isNaN(undefined);
* // => true
*
* _.isNaN(undefined);
* // => false
*/
function isNaN(value) {
// `NaN` as a primitive is the only value that is not equal to itself
// (perform the `[[Class]]` check first to avoid errors with some host objects in IE)
return isNumber(value) && value != +value;
}
/**
* Checks if `value` is `null`.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `null`, else `false`.
* @example
*
* _.isNull(null);
* // => true
*
* _.isNull(void 0);
* // => false
*/
function isNull(value) {
return value === null;
}
/**
* Checks if `value` is a number.
*
* Note: `NaN` is considered a number. See the [ES5 spec](http://es5.github.io/#x8.5)
* for more details.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a number, else `false`.
* @example
*
* _.isNumber(8.4);
* // => true
*
* _.isNumber(NaN);
* // => true
*
* _.isNumber('8.4');
* // => false
*/
function isNumber(value) {
var type = typeof value;
return type == 'number' ||
value && type == 'object' && toString.call(value) == numberClass || false;
}
/**
* Checks if `value` is an object created by the `Object` constructor.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
* @example
*
* function Shape() {
* this.x = 0;
* this.y = 0;
* }
*
* _.isPlainObject(new Shape);
* // => false
*
* _.isPlainObject([1, 2, 3]);
* // => false
*
* _.isPlainObject({ 'x': 0, 'y': 0 });
* // => true
*/
var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) {
return false;
}
var valueOf = value.valueOf,
objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
return objProto
? (value == objProto || getPrototypeOf(value) == objProto)
: shimIsPlainObject(value);
};
/**
* Checks if `value` is a regular expression.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a regular expression, else `false`.
* @example
*
* _.isRegExp(/abc/);
* // => true
*
* _.isRegExp('/abc/');
* // => false
*/
function isRegExp(value) {
var type = typeof value;
return value && (type == 'function' || type == 'object') &&
toString.call(value) == regexpClass || false;
}
/**
* Checks if `value` is a string.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a string, else `false`.
* @example
*
* _.isString('abc');
* // => true
*
* _.isString(1);
* // => false
*/
function isString(value) {
return typeof value == 'string' ||
value && typeof value == 'object' && toString.call(value) == stringClass || false;
}
/**
* Checks if `value` is `undefined`.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
* @example
*
* _.isUndefined(void 0);
* // => true
*
* _.isUndefined(null);
* // => false
*/
function isUndefined(value) {
return typeof value == 'undefined';
}
/**
* Creates an array of the own enumerable property names of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns the array of property names.
* @example
*
* function Shape() {
* this.x = 0;
* this.y = 0;
* }
*
* Shape.prototype.z = 0;
*
* _.keys(new Shape);
* // => ['x', 'y'] (property order is not guaranteed across environments)
*/
var keys = !nativeKeys ? shimKeys : function(object) {
var length = object ? object.length : 0;
Eif (typeof length == 'number' && length > 0) {
return shimKeys(object);
}
return isObject(object) ? nativeKeys(object) : [];
};
/**
* Creates an array of the own and inherited enumerable property names of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns the array of property names.
* @example
*
* function Shape() {
* this.x = 0;
* this.y = 0;
* }
*
* Shape.prototype.z = 0;
*
* _.keysIn(new Shape);
* // => ['x', 'y', 'z'] (property order is not guaranteed across environments)
*/
function keysIn(object) {
Iif (!isObject(object)) {
return [];
}
var length = object.length;
length = (typeof length == 'number' && length > 0 &&
(isArray(object) || (support.nonEnumStrings && isString(object)) ||
(support.nonEnumArgs && isArguments(object))) && length) >>> 0;
var keyIndex,
index = -1,
maxIndex = length - 1,
result = Array(length),
skipIndexes = length > 0,
skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
skipProto = support.enumPrototypes && typeof object == 'function';
while (++index < length) {
result[index] = String(index);
}
for (var key in object) {
Eif (!(skipProto && key == 'prototype') &&
!(skipErrorProps && (key == 'message' || key == 'name')) &&
!(skipIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0))) {
result.push(key);
}
}
// Lo-Dash skips the `constructor` property when it infers it's iterating
// over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
// attribute of an existing property and the `constructor` property of a
// prototype defaults to non-enumerable.
Iif (support.nonEnumShadows && object !== objectProto) {
var ctor = object.constructor;
index = -1;
length = shadowedProps.length;
if (object === (ctor && ctor.prototype)) {
var className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object),
nonEnum = nonEnumProps[className];
}
while (++index < length) {
key = shadowedProps[index];
if (!(nonEnum && nonEnum[key]) && hasOwnProperty.call(object, key)) {
result.push(key);
}
}
}
return result;
}
/**
* Creates an object with the same keys as `object` and values generated by
* running each own enumerable property of `object` through the callback.
* The callback is bound to `thisArg` and invoked with three arguments;
* (value, key, object).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is provided for `callback` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
* to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the new mapped object.
* @example
*
* _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
* // => { 'a': 3, 'b': 6, 'c': 9 }
*
* var characters = {
* 'fred': { 'name': 'fred', 'age': 40 },
* 'pebbles': { 'name': 'pebbles', 'age': 1 }
* };
*
* // using "_.pluck" callback shorthand
* _.mapValues(characters, 'age');
* // => { 'fred': 40, 'pebbles': 1 }
*/
function mapValues(object, callback, thisArg) {
var result = {};
callback = lodash.createCallback(callback, thisArg, 3);
baseForOwn(object, function(value, key, object) {
result[key] = callback(value, key, object);
});
return result;
}
/**
* Recursively merges own enumerable properties of the source object(s), that
* don't resolve to `undefined` into the destination object. Subsequent sources
* will overwrite property assignments of previous sources. If a callback is
* provided it will be executed to produce the merged values of the destination
* and source properties. If the callback returns `undefined` merging will
* be handled by the method instead. The callback is bound to `thisArg` and
* invoked with two arguments; (objectValue, sourceValue).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
* @param {...Object} [source] The source objects.
* @param {Function} [callback] The function to customize merging properties.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the destination object.
* @example
*
* var names = {
* 'characters': [
* { 'name': 'barney' },
* { 'name': 'fred' }
* ]
* };
*
* var ages = {
* 'characters': [
* { 'age': 36 },
* { 'age': 40 }
* ]
* };
*
* _.merge(names, ages);
* // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
*
* var food = {
* 'fruits': ['apple'],
* 'vegetables': ['beet']
* };
*
* var otherFood = {
* 'fruits': ['banana'],
* 'vegetables': ['carrot']
* };
*
* _.merge(food, otherFood, function(a, b) {
* return _.isArray(a) ? a.concat(b) : undefined;
* });
* // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
*/
function merge(object, source, guard) {
if (!object) {
return object;
}
var args = arguments,
length = args.length,
type = typeof guard;
// enables use as a callback for functions like `_.reduce`
if ((type == 'number' || type == 'string') && args[3] && args[3][guard] === source) {
length = 2;
}
// juggle arguments
if (length > 3 && typeof args[length - 2] == 'function') {
var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
} else if (length > 2 && typeof args[length - 1] == 'function') {
callback = args[--length];
}
var sources = slice(args, 1, length),
index = -1,
stackA = [],
stackB = [];
while (++index < length) {
baseMerge(object, sources[index], callback, stackA, stackB);
}
return object;
}
/**
* Creates a shallow clone of `object` excluding the specified properties.
* Property names may be specified as individual arguments or as arrays of
* property names. If a predicate is provided it will be executed for each
* property of `object` omitting the properties the predicate returns truthy
* for. The predicate is bound to `thisArg` and invoked with three arguments;
* (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
* @param {Function|...string|string[]} [predicate] The function called per
* iteration or property names to omit, specified as individual property
* names or arrays of property names.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {Object} Returns the new object.
* @example
*
* _.omit({ 'name': 'fred', 'age': 40 }, 'age');
* // => { 'name': 'fred' }
*
* _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
* return typeof value == 'number';
* });
* // => { 'name': 'fred' }
*/
function omit(object, predicate, thisArg) {
if (typeof predicate == 'function') {
predicate = lodash.createCallback(predicate, thisArg, 3);
return pick(object, negate(predicate));
}
var omitProps = baseFlatten(arguments, true, false, 1),
length = omitProps.length;
while (length--) {
omitProps[length] = String(omitProps[length]);
}
return pick(object, baseDifference(keysIn(object), omitProps));
}
/**
* Creates a two dimensional array of a given object's key-value pairs,
* i.e. `[[key1, value1], [key2, value2]]`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns new array of key-value pairs.
* @example
*
* _.pairs({ 'barney': 36, 'fred': 40 });
* // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
*/
function pairs(object) {
var index = -1,
props = keys(object),
length = props.length,
result = Array(length);
while (++index < length) {
var key = props[index];
result[index] = [key, object[key]];
}
return result;
}
/**
* Creates a shallow clone of `object` composed of the specified properties.
* Property names may be specified as individual arguments or as arrays of
* property names. If a predicate is provided it will be executed for each
* property of `object` picking the properties the predicate returns truthy
* for. The predicate is bound to `thisArg` and invoked with three arguments;
* (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
* @param {Function|...string|string[]} [predicate] The function called per
* iteration or property names to pick, specified as individual property
* names or arrays of property names.
* @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {Object} Returns the new object.
* @example
*
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
* // => { 'name': 'fred' }
*
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
* return key.charAt(0) != '_';
* });
* // => { 'name': 'fred' }
*/
function pick(object, predicate, thisArg) {
var result = {};
if (typeof predicate != 'function') {
var index = -1,
props = baseFlatten(arguments, true, false, 1),
length = isObject(object) ? props.length : 0;
while (++index < length) {
var key = props[index];
if (key in object) {
result[key] = object[key];
}
}
} else {
predicate = lodash.createCallback(predicate, thisArg, 3);
baseForIn(object, function(value, key, object) {
if (predicate(value, key, object)) {
result[key] = value;
}
});
}
return result;
}
/**
* An alternative to `_.reduce`; this method transforms `object` to a new
* `accumulator` object which is the result of running each of its own
* enumerable properties through a callback, with each callback execution
* potentially mutating the `accumulator` object. The callback is bound to
* `thisArg` and invoked with four arguments; (accumulator, value, key, object).
* Callbacks may exit iteration early by explicitly returning `false`.
*
* @static
* @memberOf _
* @category Objects
* @param {Array|Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [accumulator] The custom accumulator value.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the accumulated value.
* @example
*
* var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8], function(result, num) {
* num *= num;
* if (num % 2) {
* return result.push(num) < 3;
* }
* });
* // => [1, 9, 25]
*
* var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
* result[key] = num * 3;
* });
* // => { 'a': 3, 'b': 6, 'c': 9 }
*/
function transform(object, callback, accumulator, thisArg) {
var isArr = isArray(object);
if (accumulator == null) {
if (isArr) {
accumulator = [];
} else {
var ctor = object && object.constructor,
proto = ctor && ctor.prototype;
accumulator = baseCreate(proto);
}
}
if (callback) {
callback = lodash.createCallback(callback, thisArg, 4);
(isArr ? baseEach : baseForOwn)(object, function(value, index, object) {
return callback(accumulator, value, index, object);
});
}
return accumulator;
}
/**
* Creates an array of the own enumerable property values of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns the array of property values.
* @example
*
* function Shape(x, y) {
* this.x = x;
* this.y = y;
* }
*
* Shape.prototype.z = 0;
*
* _.values(new Shape(2, 1));
* // => [2, 1] (property order is not guaranteed across environments)
*/
function values(object) {
return baseValues(object, keys);
}
/**
* Creates an array of the own and inherited enumerable property values
* of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns the array of property values.
* @example
*
* function Shape(x, y) {
* this.x = x;
* this.y = y;
* }
*
* Shape.prototype.z = 0;
*
* _.valuesIn(new Shape(2, 1));
* // => [2, 1, 0] (property order is not guaranteed across environments)
*/
function valuesIn(object) {
return baseValues(object, keysIn);
}
/*--------------------------------------------------------------------------*/
/**
* Converts `string` to camel case.
* See [Wikipedia](http://en.wikipedia.org/wiki/CamelCase) for more details.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to camel case.
* @returns {string} Returns the camel cased string.
* @example
*
* _.camelCase('Hello world');
* // => 'helloWorld'
*
* _.camelCase('--hello-world');
* // => 'helloWorld'
*
* _.camelCase('__hello_world__');
* // => 'helloWorld'
*/
var camelCase = createCompounder(function(result, word, index) {
if (!index && reAllCaps.test(word)) {
return result + word.toLowerCase();
}
return result + (word.charAt(0)[index ? 'toUpperCase' : 'toLowerCase']() + word.slice(1));
});
/**
* Capitalizes the first character of `string`.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to capitalize.
* @returns {string} Returns the capitalized string.
* @example
*
* _.capitalize('fred');
* // => 'Fred'
*/
function capitalize(string) {
if (string == null) {
return '';
}
string = String(string);
return string.charAt(0).toUpperCase() + string.slice(1);
}
/**
* Checks if `string` ends with a given target string.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to search.
* @param {string} [target] The string to search for.
* @param {number} [position=string.length] The position to search from.
* @returns {boolean} Returns `true` if the given string ends with the
* target string, else `false`.
* @example
*
* _.endsWith('abc', 'c');
* // => true
*
* _.endsWith('abc', 'b');
* // => false
*
* _.endsWith('abc', 'b', 2);
* // => true
*/
function endsWith(string, target, position) {
string = string == null ? '' : String(string);
target = String(target);
var length = string.length;
position = (typeof position == 'undefined' ? length : nativeMin(position < 0 ? 0 : (+position || 0), length)) - target.length;
return position >= 0 && string.indexOf(target, position) == position;
}
/**
* Converts the characters "&", "<", ">", '"', and "'" in `string` to
* their corresponding HTML entities.
*
* Note: No other characters are escaped. To escape additional characters
* use a third-party library like [_he_](http://mths.be/he).
*
* When working with HTML you should always quote attribute values to reduce
* XSS vectors. See [Ryan Grove's article](http://wonko.com/post/html-escaping)
* for more details.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to escape.
* @returns {string} Returns the escaped string.
* @example
*
* _.escape('fred, barney, & pebbles');
* // => 'fred, barney, & pebbles'
*/
function escape(string) {
return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
}
/**
* Escapes the `RegExp` special characters "\", "^", "$", ".", "|", "?", "*",
* "+", "(", ")", "[", "]", "{" and "}" in `string`.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to escape.
* @returns {string} Returns the escaped string.
* @example
*
* _.escapeRegExp('[lodash](http://lodash.com)');
* // => '\[lodash\]\(http://lodash\.com\)'
*/
function escapeRegExp(string) {
return string == null ? '' : String(string).replace(reRegExpChars, '\\$&');
}
/**
* Converts `string` to kebab case (a.k.a. spinal case).
* See [Wikipedia](http://en.wikipedia.org/wiki/Letter_case#Computers) for
* more details.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to kebab case.
* @returns {string} Returns the kebab cased string.
* @example
*
* _.kebabCase('Hello world');
* // => 'hello-world'
*
* _.kebabCase('helloWorld');
* // => 'hello-world'
*
* _.kebabCase('__hello_world__');
* // => 'hello-world'
*/
var kebabCase = createCompounder(function(result, word, index) {
return result + (index ? '-' : '') + word.toLowerCase();
});
/**
* Pads `string` on the left and right sides if it is shorter then the given
* padding length. The `chars` string may be truncated if the number of padding
* characters can't be evenly divided by the padding length.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to pad.
* @param {number} [length=0] The padding length.
* @param {string} [chars=' '] The string used as padding.
* @returns {string} Returns the padded string.
* @example
*
* _.pad('abc', 8);
* // => ' abc '
*
* _.pad('abc', 8, '_-');
* // => '_-abc_-_'
*
* _.pad('abc', 3);
* // => 'abc'
*/
function pad(string, length, chars) {
string = string == null ? '' : String(string);
length = +length;
var strLength = string.length;
if (strLength >= length || !nativeIsFinite(length)) {
return string;
}
var mid = (length - strLength) / 2,
leftLength = floor(mid),
rightLength = ceil(mid);
chars = createPad('', rightLength, chars);
return chars.slice(0, leftLength) + string + chars;
}
/**
* Pads `string` on the left side if it is shorter then the given padding
* length. The `chars` string may be truncated if the number of padding
* characters exceeds the padding length.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to pad.
* @param {number} [length=0] The padding length.
* @param {string} [chars=' '] The string used as padding.
* @returns {string} Returns the padded string.
* @example
*
* _.padLeft('abc', 6);
* // => ' abc'
*
* _.padLeft('abc', 6, '_-');
* // => '_-_abc'
*
* _.padLeft('abc', 3);
* // => 'abc'
*/
function padLeft(string, length, chars) {
string = string == null ? '' : String(string);
return createPad(string, length, chars) + string;
}
/**
* Pads `string` on the right side if it is shorter then the given padding
* length. The `chars` string may be truncated if the number of padding
* characters exceeds the padding length.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to pad.
* @param {number} [length=0] The padding length.
* @param {string} [chars=' '] The string used as padding.
* @returns {string} Returns the padded string.
* @example
*
* _.padRight('abc', 6);
* // => 'abc '
*
* _.padRight('abc', 6, '_-');
* // => 'abc_-_'
*
* _.padRight('abc', 3);
* // => 'abc'
*/
function padRight(string, length, chars) {
string = string == null ? '' : String(string);
return string + createPad(string, length, chars);
}
/**
* Repeats the given string `n` times.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to repeat.
* @param {number} [n=0] The number of times to repeat the string.
* @returns {string} Returns the repeated string.
* @example
*
* _.repeat('*', 3);
* // => '***'
*
* _.repeat('abc', 2);
* // => 'abcabc'
*
* _.repeat('abc', 0);
* // => ''
*/
function repeat(string, n) {
var result = '';
n = +n;
if (n < 1 || string == null || !nativeIsFinite(n)) {
return result;
}
string = String(string);
do {
if (n % 2) {
result += string;
}
n = floor(n / 2);
string += string;
} while (n);
return result;
}
/**
* Converts `string` to snake case.
* See [Wikipedia](http://en.wikipedia.org/wiki/Snake_case) for more details.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to snake case.
* @returns {string} Returns the snake cased string.
* @example
*
* _.snakeCase('Hello world');
* // => 'hello_world'
*
* _.snakeCase('--hello-world');
* // => 'hello_world'
*
* _.snakeCase('helloWorld');
* // => 'hello_world'
*/
var snakeCase = createCompounder(function(result, word, index) {
return result + (index ? '_' : '') + word.toLowerCase();
});
/**
* Checks if `string` starts with a given target string.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to search.
* @param {string} [target] The string to search for.
* @param {number} [position=0] The position to search from.
* @returns {boolean} Returns `true` if the given string starts with the
* target string, else `false`.
* @example
*
* _.startsWith('abc', 'a');
* // => true
*
* _.startsWith('abc', 'b');
* // => false
*
* _.startsWith('abc', 'b', 1);
* // => true
*/
function startsWith(string, target, position) {
string = string == null ? '' : String(string);
position = typeof position == 'undefined' ? 0 : nativeMin(position < 0 ? 0 : (+position || 0), string.length);
return string.lastIndexOf(target, position) == position;
}
/**
* Creates a compiled template function that can interpolate data properties
* in "interpolate" delimiters, HTML-escaped interpolated data properties in
* "escape" delimiters, and execute JavaScript in "evaluate" delimiters. If
* a data object is provided the interpolated template string will be returned.
* Data properties may be accessed as free variables in the template. If a
* settings object is provided it will override `_.templateSettings` for the
* template.
*
* Note: In the development build, `_.template` utilizes `sourceURL`s for easier debugging.
* See the [HTML5 Rocks article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
* for more details.
*
* For more information on precompiling templates see
* [Lo-Dash's custom builds documentation](http://lodash.com/custom-builds).
*
* For more information on Chrome extension sandboxes see
* [Chrome's extensions documentation](http://developer.chrome.com/stable/extensions/sandboxingEval.html).
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The template string.
* @param {Object} [data] The data object used to populate the template string.
* @param {Object} [options] The options object.
* @param {RegExp} [options.escape] The HTML "escape" delimiter.
* @param {RegExp} [options.evaluate] The "evaluate" delimiter.
* @param {Object} [options.imports] An object to import into the template as local variables.
* @param {RegExp} [options.interpolate] The "interpolate" delimiter.
* @param {string} [options.sourceURL] The `sourceURL` of the template's compiled source.
* @param {string} [options.variable] The data object variable name.
* @returns {Function|string} Returns the interpolated string if a data object
* is provided, else the compiled template function.
* @example
*
* // using the "interpolate" delimiter to create a compiled template
* var compiled = _.template('hello <%= name %>');
* compiled({ 'name': 'fred' });
* // => 'hello fred'
*
* // using the HTML "escape" delimiter to escape data property values
* _.template('<b><%- value %></b>', { 'value': '<script>' });
* // => '<b><script></b>'
*
* // using the "evaluate" delimiter to execute JavaScript and generate HTML
* var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
* _.template(list, { 'people': ['fred', 'barney'] });
* // => '<li>fred</li><li>barney</li>'
*
* // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
* _.template('hello ${ name }', { 'name': 'pebbles' });
* // => 'hello pebbles'
*
* // using the internal `print` function in "evaluate" delimiters
* _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
* // => 'hello barney!'
*
* // using a custom template delimiters
* _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
* _.template('hello {{ name }}!', { 'name': 'mustache' });
* // => 'hello mustache!'
*
* // using the `imports` option to import jQuery
* var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
* _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
* // => '<li>fred</li><li>barney</li>'
*
* // using the `sourceURL` option to specify a custom `sourceURL` for the template
* var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
* compiled(data);
* // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
*
* // using the `variable` option to ensure a with-statement isn't used in the compiled template
* var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
* compiled.source;
* // => function(data) {
* var __t, __p = '', __e = _.escape;
* __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
* return __p;
* }
*
* // using the `source` property to inline compiled templates for meaningful
* // line numbers in error messages and a stack trace
* fs.writeFileSync(path.join(cwd, 'jst.js'), '\
* var JST = {\
* "main": ' + _.template(mainText).source + '\
* };\
* ');
*/
function template(string, data, options) {
// based on John Resig's `tmpl` implementation
// http://ejohn.org/blog/javascript-micro-templating/
// and Laura Doktorova's doT.js
// https://github.com/olado/doT
var settings = lodash.templateSettings;
options = defaults({}, options, settings);
string = String(string == null ? '' : string);
var imports = defaults({}, options.imports, settings.imports),
importsKeys = keys(imports),
importsValues = values(imports);
var isEscaping,
isEvaluating,
index = 0,
interpolate = options.interpolate || reNoMatch,
source = "__p += '";
// compile the regexp to match each delimiter
var reDelimiters = RegExp(
(options.escape || reNoMatch).source + '|' +
interpolate.source + '|' +
(interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
(options.evaluate || reNoMatch).source + '|$'
, 'g');
string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
interpolateValue || (interpolateValue = esTemplateValue);
// escape characters that cannot be included in string literals
source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);
// replace delimiters with snippets
if (escapeValue) {
isEscaping = true;
source += "' +\n__e(" + escapeValue + ") +\n'";
}
if (evaluateValue) {
isEvaluating = true;
source += "';\n" + evaluateValue + ";\n__p += '";
}
if (interpolateValue) {
source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
}
index = offset + match.length;
// the JS engine embedded in Adobe products requires returning the `match`
// string in order to produce the correct `offset` value
return match;
});
source += "';\n";
// if `variable` is not specified, wrap a with-statement around the generated
// code to add the data object to the top of the scope chain
var variable = options.variable;
if (!variable) {
source = 'with (obj) {\n' + source + '\n}\n';
}
// cleanup code by stripping empty strings
source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
.replace(reEmptyStringMiddle, '$1')
.replace(reEmptyStringTrailing, '$1;');
// frame code as the function body
source = 'function(' + (variable || 'obj') + ') {\n' +
(variable
? ''
: 'obj || (obj = {});\n'
) +
"var __t, __p = ''" +
(isEscaping
? ', __e = _.escape'
: ''
) +
(isEvaluating
? ', __j = Array.prototype.join;\n' +
"function print() { __p += __j.call(arguments, '') }\n"
: ';\n'
) +
source +
'return __p\n}';
// Use a `sourceURL` for easier debugging.
// http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
try {
var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
} catch(e) {
e.source = source;
throw e;
}
if (data) {
return result(data);
}
// provide the compiled function's source by its `toString` method, in
// supported environments, or the `source` property as a convenience for
// inlining compiled templates during the build process
result.source = source;
return result;
}
/**
* Removes leading and trailing whitespace or specified characters from `string`.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
* @example
*
* _.trim(' fred ');
* // => 'fred'
*
* _.trim('-_-fred-_-', '_-');
* // => 'fred'
*/
var trim = !nativeTrim ? shimTrim : function(string, chars) {
if (string == null) {
return '';
}
return chars == null ? nativeTrim.call(string) : shimTrim(string, chars);
};
/**
* Removes leading whitespace or specified characters from `string`.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
* @example
*
* _.trimLeft(' fred ');
* // => 'fred '
*
* _.trimLeft('-_-fred-_-', '_-');
* // => 'fred-_-'
*/
var trimLeft = !nativeTrimLeft ? shimTrimLeft : function(string, chars) {
if (string == null) {
return '';
}
return chars == null ? nativeTrimLeft.call(string) : shimTrimLeft(string, chars);
};
/**
* Removes trailing whitespace or specified characters from `string`.
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
* @example
*
* _.trimRight(' fred ');
* // => ' fred'
*
* _.trimRight('-_-fred-_-', '_-');
* // => '-_-fred'
*/
var trimRight = !nativeTrimRight ? shimTrimRight : function(string, chars) {
if (string == null) {
return '';
}
return chars == null ? nativeTrimRight.call(string) : shimTrimRight(string, chars);
};
/**
* Truncates `string` if it is longer than the given maximum string length.
* The last characters of the truncated string will be replaced with the
* omission string which defaults to "...".
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to truncate.
* @param {Object|number} [options] The options object or maximum string length.
* @param {number} [options.length=30] The maximum string length.
* @param {string} [options.omission='...'] The string used to indicate text is omitted.
* @param {RegExp|string} [options.separator] The separator pattern to truncate to.
* @returns {string} Returns the truncated string.
* @example
*
* _.truncate('hi-diddly-ho there, neighborino');
* // => 'hi-diddly-ho there, neighbo...'
*
* _.truncate('hi-diddly-ho there, neighborino', 24);
* // => 'hi-diddly-ho there, n...'
*
* _.truncate('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': ' ' });
* // => 'hi-diddly-ho there,...'
*
* _.truncate('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': /,? +/ });
* //=> 'hi-diddly-ho there...'
*
* _.truncate('hi-diddly-ho there, neighborino', { 'omission': ' [...]' });
* // => 'hi-diddly-ho there, neig [...]'
*/
function truncate(string, options) {
var length = 30,
omission = '...';
if (options && isObject(options)) {
var separator = 'separator' in options ? options.separator : separator;
length = 'length' in options ? +options.length || 0 : length;
omission = 'omission' in options ? String(options.omission) : omission;
}
else if (options != null) {
length = +options || 0;
}
string = string == null ? '' : String(string);
if (length >= string.length) {
return string;
}
var end = length - omission.length;
if (end < 1) {
return omission;
}
var result = string.slice(0, end);
if (separator == null) {
return result + omission;
}
if (isRegExp(separator)) {
if (string.slice(end).search(separator)) {
var match,
newEnd,
substring = string.slice(0, end);
if (!separator.global) {
separator = RegExp(separator.source, (reFlags.exec(separator) || '') + 'g');
}
separator.lastIndex = 0;
while ((match = separator.exec(substring))) {
newEnd = match.index;
}
result = result.slice(0, newEnd == null ? end : newEnd);
}
} else if (string.indexOf(separator, end) != end) {
var index = result.lastIndexOf(separator);
if (index > -1) {
result = result.slice(0, index);
}
}
return result + omission;
}
/**
* The inverse of `_.escape`; this method converts the HTML entities
* `&`, `<`, `>`, `"`, and `'` in `string` to their
* corresponding characters.
*
* Note: No other HTML entities are unescaped. To unescape additional HTML
* entities use a third-party library like [_he_](http://mths.be/he).
*
* @static
* @memberOf _
* @category Strings
* @param {string} [string=''] The string to unescape.
* @returns {string} Returns the unescaped string.
* @example
*
* _.unescape('fred, barney & pebbles');
* // => 'fred, barney & pebbles'
*/
function unescape(string) {
if (string == null) {
return '';
}
string = String(string);
return string.indexOf(';') < 0 ? string : string.replace(reEscapedHtml, unescapeHtmlChar);
}
/*--------------------------------------------------------------------------*/
/**
* Creates a function that returns `value`.
*
* @static
* @memberOf _
* @category Utilities
* @param {*} value The value to return from the new function.
* @returns {Function} Returns the new function.
* @example
*
* var object = { 'name': 'fred' };
* var getter = _.constant(object);
* getter() === object;
* // => true
*/
function constant(value) {
return function() {
return value;
};
}
/**
* Creates a function bound to an optional `thisArg`. If `func` is a property
* name the created callback will return the property value for a given element.
* If `func` is an object the created callback will return `true` for elements
* that contain the equivalent object properties, otherwise it will return `false`.
*
* @static
* @memberOf _
* @alias callback
* @category Utilities
* @param {*} [func=identity] The value to convert to a callback.
* @param {*} [thisArg] The `this` binding of the created callback.
* @param {number} [argCount] The number of arguments the callback accepts.
* @returns {Function} Returns the new function.
* @example
*
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
* ];
*
* // wrap to create custom callback shorthands
* _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
* var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
* return !match ? func(callback, thisArg) : function(object) {
* return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
* };
* });
*
* _.filter(characters, 'age__gt38');
* // => [{ 'name': 'fred', 'age': 40 }]
*/
function createCallback(func, thisArg, argCount) {
var type = typeof func;
if (type == 'function' || func == null) {
return (typeof thisArg == 'undefined' || !('prototype' in func)) &&
func || baseCreateCallback(func, thisArg, argCount);
}
// handle "_.pluck" and "_.where" style callback shorthands
return type != 'object' ? property(func) : matches(func);
}
/**
* This method returns the first argument provided to it.
*
* @static
* @memberOf _
* @category Utilities
* @param {*} value Any value.
* @returns {*} Returns `value`.
* @example
*
* var object = { 'name': 'fred' };
* _.identity(object) === object;
* // => true
*/
function identity(value) {
return value;
}
/**
* Creates a "_.where" style predicate function which performs a deep comparison
* between a given object and the `source` object, returning `true` if the given
* object has equivalent property values, else `false`.
*
* @static
* @memberOf _
* @category Utilities
* @param {Object} source The object of property values to match.
* @returns {Function} Returns the new function.
* @example
*
* var characters = [
* { 'name': 'fred', 'age': 40 },
* { 'name': 'barney', 'age': 36 }
* ];
*
* var matchesAge = _.matches({ 'age': 36 });
*
* _.filter(characters, matchesAge);
* // => [{ 'name': 'barney', 'age': 36 }]
*
* _.find(characters, matchesAge);
* // => { 'name': 'barney', 'age': 36 }
*/
function matches(source) {
source || (source = {});
var props = keys(source),
propsLength = props.length,
key = props[0],
value = source[key];
// fast path the common case of providing an object with a single
// property containing a primitive value
if (propsLength == 1 && value === value && !isObject(value)) {
return function(object) {
if (!(object && hasOwnProperty.call(object, key))) {
return false;
}
// treat `-0` vs. `+0` as not equal
var other = object[key];
return value === other && (value !== 0 || (1 / value == 1 / other));
};
}
return function(object) {
var length = propsLength;
if (length && !object) {
return false;
}
var result = true;
while (length--) {
var key = props[length];
if (!(result = hasOwnProperty.call(object, key) &&
baseIsEqual(object[key], source[key], null, true))) {
break;
}
}
return result;
};
}
/**
* Adds function properties of a source object to the destination object.
* If `object` is a function methods will be added to its prototype as well.
*
* @static
* @memberOf _
* @category Utilities
* @param {Function|Object} [object=lodash] object The destination object.
* @param {Object} source The object of functions to add.
* @param {Object} [options] The options object.
* @param {boolean} [options.chain=true] Specify whether the functions added
* are chainable.
* @example
*
* function vowels(string) {
* return _.filter(string, function(v) {
* return /[aeiou]/i.test(v);
* });
* }
*
* _.mixin({ 'vowels': vowels });
* _.vowels('fred');
* // => ['e']
*
* _('fred').vowels().value();
* // => ['e']
*
* _.mixin({ 'vowels': vowels }, { 'chain': false });
* _('fred').vowels();
* // => ['e']
*/
function mixin(object, source, options) {
var chain = true,
methodNames = source && functions(source);
Eif (!source || (!options && !methodNames.length)) {
Eif (options == null) {
options = source;
}
source = object;
object = lodash;
methodNames = functions(source);
}
if (options === false) {
chain = false;
} else Iif (isObject(options) && 'chain' in options) {
chain = options.chain;
}
var index = -1,
isFunc = isFunction(object),
length = methodNames ? methodNames.length : 0;
while (++index < length) {
var methodName = methodNames[index],
func = object[methodName] = source[methodName];
Eif (isFunc) {
object.prototype[methodName] = (function(func) {
return function() {
var chainAll = this.__chain__,
value = this.__wrapped__,
args = [value];
push.apply(args, arguments);
var result = func.apply(object, args);
if (chain || chainAll) {
if (value === result && isObject(result)) {
return this;
}
result = new object(result);
result.__chain__ = chainAll;
}
return result;
};
}(func));
}
}
}
/**
* Reverts the `_` variable to its previous value and returns a reference to
* the `lodash` function.
*
* @static
* @memberOf _
* @category Utilities
* @returns {Function} Returns the `lodash` function.
* @example
*
* var lodash = _.noConflict();
*/
function noConflict() {
context._ = oldDash;
return this;
}
/**
* A no-operation function.
*
* @static
* @memberOf _
* @category Utilities
* @example
*
* var object = { 'name': 'fred' };
* _.noop(object) === undefined;
* // => true
*/
function noop() {
// no operation performed
}
/**
* Gets the number of milliseconds that have elapsed since the Unix epoch
* (1 January 1970 00:00:00 UTC).
*
* @static
* @memberOf _
* @category Utilities
* @example
*
* var stamp = _.now();
* _.defer(function() { console.log(_.now() - stamp); });
* // => logs the number of milliseconds it took for the deferred function to be called
*/
var now = nativeNow || function() {
return new Date().getTime();
};
/**
* Converts `value` to an integer of the specified radix. If `radix` is
* `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal,
* in which case a `radix` of `16` is used.
*
* Note: This method avoids differences in native ES3 and ES5 `parseInt`
* implementations. See the [ES5 spec](http://es5.github.io/#E)
* for more details.
*
* @static
* @memberOf _
* @category Utilities
* @param {string} value The value to parse.
* @param {number} [radix] The radix used to interpret the value to parse.
* @returns {number} Returns the converted integer.
* @example
*
* _.parseInt('08');
* // => 8
*/
var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
// Firefox < 21 and Opera < 15 follow ES3 for `parseInt` and
// Chrome fails to trim leading <BOM> whitespace characters.
// See https://code.google.com/p/v8/issues/detail?id=3109
value = trim(value);
return nativeParseInt(value, +radix || (reHexPrefix.test(value) ? 16 : 10));
};
/**
* Creates a "_.pluck" style function which returns the `key` value of a
* given object.
*
* @static
* @memberOf _
* @category Utilities
* @param {string} key The name of the property to retrieve.
* @returns {Function} Returns the new function.
* @example
*
* var characters = [
* { 'name': 'fred', 'age': 40 },
* { 'name': 'barney', 'age': 36 }
* ];
*
* var getName = _.property('name');
*
* _.map(characters, getName);
* // => ['barney', 'fred']
*
* _.sortBy(characters, getName);
* // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
*/
function property(key) {
return function(object) {
return object == null ? undefined : object[key];
};
}
/**
* Produces a random number between `min` and `max` (inclusive). If only one
* argument is provided a number between `0` and the given number will be
* returned. If `floating` is truthy or either `min` or `max` are floats a
* floating-point number will be returned instead of an integer.
*
* @static
* @memberOf _
* @category Utilities
* @param {number} [min=0] The minimum possible value.
* @param {number} [max=1] The maximum possible value.
* @param {boolean} [floating=false] Specify returning a floating-point number.
* @returns {number} Returns the random number.
* @example
*
* _.random(0, 5);
* // => an integer between 0 and 5
*
* _.random(5);
* // => also an integer between 0 and 5
*
* _.random(5, true);
* // => a floating-point number between 0 and 5
*
* _.random(1.2, 5.2);
* // => a floating-point number between 1.2 and 5.2
*/
function random(min, max, floating) {
var noMin = min == null,
noMax = max == null;
if (floating == null) {
if (noMax && typeof min == 'boolean') {
floating = min;
min = 1;
}
else if (typeof max == 'boolean') {
floating = max;
noMax = true;
}
}
if (noMin && noMax) {
max = 1;
noMax = false;
}
min = +min || 0;
if (noMax) {
max = min;
min = 0;
} else {
max = +max || 0;
}
if (floating || min % 1 || max % 1) {
var rand = nativeRandom();
return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
}
return baseRandom(min, max);
}
/**
* Creates an array of numbers (positive and/or negative) progressing from
* `start` up to but not including `end`. If `start` is less than `stop` a
* zero-length range is created unless a negative `step` is specified.
*
* @static
* @memberOf _
* @category Utilities
* @param {number} [start=0] The start of the range.
* @param {number} end The end of the range.
* @param {number} [step=1] The value to increment or decrement by.
* @returns {Array} Returns the new array of numbers.
* @example
*
* _.range(4);
* // => [0, 1, 2, 3]
*
* _.range(1, 5);
* // => [1, 2, 3, 4]
*
* _.range(0, 20, 5);
* // => [0, 5, 10, 15]
*
* _.range(0, -4, -1);
* // => [0, -1, -2, -3]
*
* _.range(1, 4, 0);
* // => [1, 1, 1]
*
* _.range(0);
* // => []
*/
function range(start, end, step) {
start = +start || 0;
step = step == null ? 1 : (+step || 0);
if (end == null) {
end = start;
start = 0;
} else {
end = +end || 0;
}
// use `Array(length)` so engines like Chakra and V8 avoid slower modes
// http://youtu.be/XAqIpGU8ZZk#t=17m25s
var index = -1,
length = nativeMax(ceil((end - start) / (step || 1)), 0),
result = Array(length);
while (++index < length) {
result[index] = start;
start += step;
}
return result;
}
/**
* Resolves the value of property `key` on `object`. If `key` is a function
* it will be invoked with the `this` binding of `object` and its result
* returned, else the property value is returned. If `object` is `null` or
* `undefined` then `undefined` is returned. If a default value is provided
* it will be returned if the property value resolves to `undefined`.
*
* @static
* @memberOf _
* @category Utilities
* @param {Object} object The object to inspect.
* @param {string} key The name of the property to resolve.
* @param {*} [defaultValue] The value returned if the property value
* resolves to `undefined`.
* @returns {*} Returns the resolved value.
* @example
*
* var object = {
* 'name': 'fred',
* 'age': function() {
* return 40;
* }
* };
*
* _.result(object, 'name');
* // => 'fred'
*
* _.result(object, 'age');
* // => 40
*
* _.result(object, 'employer', 'slate');
* // => 'slate'
*/
function result(object, key, defaultValue) {
var value = object == null ? undefined : object[key];
if (typeof value == 'undefined') {
return defaultValue;
}
return isFunction(value) ? object[key]() : value;
}
/**
* Executes the callback `n` times, returning an array of the results
* of each callback execution. The callback is bound to `thisArg` and invoked
* with one argument; (index).
*
* @static
* @memberOf _
* @category Utilities
* @param {number} n The number of times to execute the callback.
* @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns the array of results.
* @example
*
* var diceRolls = _.times(3, _.partial(_.random, 1, 6));
* // => [3, 6, 4]
*
* _.times(3, function(n) { mage.castSpell(n); });
* // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
*
* _.times(3, function(n) { this.cast(n); }, mage);
* // => also calls `mage.castSpell(n)` three times
*/
function times(n, callback, thisArg) {
n = n < 0 ? 0 : n >>> 0;
var index = -1,
result = Array(n);
callback = baseCreateCallback(callback, thisArg, 1);
while (++index < n) {
result[index] = callback(index);
}
return result;
}
/**
* Generates a unique ID. If `prefix` is provided the ID will be appended to it.
*
* @static
* @memberOf _
* @category Utilities
* @param {string} [prefix] The value to prefix the ID with.
* @returns {string} Returns the unique ID.
* @example
*
* _.uniqueId('contact_');
* // => 'contact_104'
*
* _.uniqueId();
* // => '105'
*/
function uniqueId(prefix) {
var id = ++idCounter;
return String(prefix == null ? '' : prefix) + id;
}
/*--------------------------------------------------------------------------*/
// add functions that return wrapped values when chaining
lodash.after = after;
lodash.assign = assign;
lodash.at = at;
lodash.bind = bind;
lodash.bindAll = bindAll;
lodash.bindKey = bindKey;
lodash.chain = chain;
lodash.compact = compact;
lodash.compose = compose;
lodash.constant = constant;
lodash.countBy = countBy;
lodash.create = create;
lodash.createCallback = createCallback;
lodash.curry = curry;
lodash.debounce = debounce;
lodash.defaults = defaults;
lodash.defer = defer;
lodash.delay = delay;
lodash.difference = difference;
lodash.drop = drop;
lodash.dropRight = dropRight;
lodash.dropRightWhile = dropRightWhile;
lodash.dropWhile = dropWhile;
lodash.filter = filter;
lodash.flatten = flatten;
lodash.forEach = forEach;
lodash.forEachRight = forEachRight;
lodash.forIn = forIn;
lodash.forInRight = forInRight;
lodash.forOwn = forOwn;
lodash.forOwnRight = forOwnRight;
lodash.functions = functions;
lodash.groupBy = groupBy;
lodash.indexBy = indexBy;
lodash.initial = initial;
lodash.intersection = intersection;
lodash.invert = invert;
lodash.invoke = invoke;
lodash.keys = keys;
lodash.keysIn = keysIn;
lodash.map = map;
lodash.mapValues = mapValues;
lodash.matches = matches;
lodash.max = max;
lodash.memoize = memoize;
lodash.merge = merge;
lodash.min = min;
lodash.negate = negate;
lodash.omit = omit;
lodash.once = once;
lodash.pairs = pairs;
lodash.partial = partial;
lodash.partialRight = partialRight;
lodash.partition = partition;
lodash.pick = pick;
lodash.pluck = pluck;
lodash.property = property;
lodash.pull = pull;
lodash.range = range;
lodash.reject = reject;
lodash.remove = remove;
lodash.rest = rest;
lodash.shuffle = shuffle;
lodash.slice = slice;
lodash.sortBy = sortBy;
lodash.tap = tap;
lodash.throttle = throttle;
lodash.times = times;
lodash.toArray = toArray;
lodash.transform = transform;
lodash.union = union;
lodash.uniq = uniq;
lodash.values = values;
lodash.valuesIn = valuesIn;
lodash.where = where;
lodash.without = without;
lodash.wrap = wrap;
lodash.xor = xor;
lodash.zip = zip;
lodash.zipObject = zipObject;
// add aliases
lodash.callback = createCallback;
lodash.collect = map;
lodash.each = forEach;
lodash.eachRight = forEachRight;
lodash.extend = assign;
lodash.methods = functions;
lodash.object = zipObject;
lodash.select = filter;
lodash.tail = rest;
lodash.unique = uniq;
lodash.unzip = zip;
// add functions to `lodash.prototype`
mixin(assign({}, lodash));
/*--------------------------------------------------------------------------*/
// add functions that return unwrapped values when chaining
lodash.camelCase = camelCase;
lodash.capitalize = capitalize;
lodash.clone = clone;
lodash.cloneDeep = cloneDeep;
lodash.contains = contains;
lodash.endsWith = endsWith;
lodash.escape = escape;
lodash.escapeRegExp = escapeRegExp;
lodash.every = every;
lodash.find = find;
lodash.findIndex = findIndex;
lodash.findKey = findKey;
lodash.findLast = findLast;
lodash.findLastIndex = findLastIndex;
lodash.findLastKey = findLastKey;
lodash.has = has;
lodash.identity = identity;
lodash.indexOf = indexOf;
lodash.isArguments = isArguments;
lodash.isArray = isArray;
lodash.isBoolean = isBoolean;
lodash.isDate = isDate;
lodash.isElement = isElement;
lodash.isEmpty = isEmpty;
lodash.isEqual = isEqual;
lodash.isFinite = isFinite;
lodash.isFunction = isFunction;
lodash.isNaN = isNaN;
lodash.isNull = isNull;
lodash.isNumber = isNumber;
lodash.isObject = isObject;
lodash.isPlainObject = isPlainObject;
lodash.isRegExp = isRegExp;
lodash.isString = isString;
lodash.isUndefined = isUndefined;
lodash.kebabCase = kebabCase;
lodash.lastIndexOf = lastIndexOf;
lodash.mixin = mixin;
lodash.noConflict = noConflict;
lodash.noop = noop;
lodash.now = now;
lodash.pad = pad;
lodash.padLeft = padLeft;
lodash.padRight = padRight;
lodash.parseInt = parseInt;
lodash.random = random;
lodash.reduce = reduce;
lodash.reduceRight = reduceRight;
lodash.repeat = repeat;
lodash.result = result;
lodash.runInContext = runInContext;
lodash.size = size;
lodash.some = some;
lodash.sortedIndex = sortedIndex;
lodash.snakeCase = snakeCase;
lodash.startsWith = startsWith;
lodash.template = template;
lodash.trim = trim;
lodash.trimLeft = trimLeft;
lodash.trimRight = trimRight;
lodash.truncate = truncate;
lodash.unescape = unescape;
lodash.uniqueId = uniqueId;
// add aliases
lodash.all = every;
lodash.any = some;
lodash.detect = find;
lodash.findWhere = find;
lodash.foldl = reduce;
lodash.foldr = reduceRight;
lodash.include = contains;
lodash.inject = reduce;
mixin(function() {
var source = {}
baseForOwn(lodash, function(func, methodName) {
if (!lodash.prototype[methodName]) {
source[methodName] = func;
}
});
return source;
}(), false);
/*--------------------------------------------------------------------------*/
// add functions capable of returning wrapped and unwrapped values when chaining
lodash.first = first;
lodash.last = last;
lodash.sample = sample;
lodash.take = take;
lodash.takeRight = takeRight;
lodash.takeRightWhile = takeRightWhile;
lodash.takeWhile = takeWhile;
// add aliases
lodash.head = first;
baseForOwn(lodash, function(func, methodName) {
var callbackable = methodName !== 'sample';
if (!lodash.prototype[methodName]) {
lodash.prototype[methodName]= function(n, guard) {
var chainAll = this.__chain__,
result = func(this.__wrapped__, n, guard);
return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
? result
: new lodashWrapper(result, chainAll);
};
}
});
/*--------------------------------------------------------------------------*/
/**
* The semantic version number.
*
* @static
* @memberOf _
* @type string
*/
lodash.VERSION = version;
// add "Chaining" functions to the wrapper
lodash.prototype.chain = wrapperChain;
lodash.prototype.toJSON = wrapperValueOf;
lodash.prototype.toString = wrapperToString;
lodash.prototype.value = wrapperValueOf;
lodash.prototype.valueOf = wrapperValueOf;
// add `Array` functions that return unwrapped values
baseEach(['join', 'pop', 'shift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
var chainAll = this.__chain__,
result = func.apply(this.__wrapped__, arguments);
return chainAll
? new lodashWrapper(result, chainAll)
: result;
};
});
// add `Array` functions that return the existing wrapped value
baseEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
func.apply(this.__wrapped__, arguments);
return this;
};
});
// add `Array` functions that return new wrapped values
baseEach(['concat', 'splice'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
};
});
// avoid array-like object bugs with `Array#shift` and `Array#splice`
// in IE < 9, Firefox < 10, Narwhal, and RingoJS
Iif (!support.spliceObjects) {
baseEach(['pop', 'shift', 'splice'], function(methodName) {
var func = arrayRef[methodName],
isSplice = methodName == 'splice';
lodash.prototype[methodName] = function() {
var chainAll = this.__chain__,
value = this.__wrapped__,
result = func.apply(value, arguments);
if (value.length === 0) {
delete value[0];
}
return (chainAll || isSplice)
? new lodashWrapper(result, chainAll)
: result;
};
});
}
return lodash;
}
/*--------------------------------------------------------------------------*/
// export Lo-Dash
var _ = runInContext();
// some AMD build optimizers like r.js check for condition patterns like the following:
Eif (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
// Expose Lo-Dash to the global object even when an AMD loader is present in
// case Lo-Dash is loaded with a RequireJS shim config.
// See http://requirejs.org/docs/api.html#config-shim
root._ = _;
// define as an anonymous module so, through path mapping, it can be
// referenced as the "underscore" module
define(function() {
return _;
});
}
// check for `exports` after `define` in case a build optimizer adds an `exports` object
else if (freeExports && freeModule) {
// in Node.js or RingoJS
if (moduleExports) {
(freeModule.exports = _)._ = _;
}
// in Narwhal or Rhino -require
else {
freeExports._ = _;
}
}
else {
// in a browser or Rhino
root._ = _;
}
}.call(this));
|
| File | Statements | Branches | Functions | Lines | |||||
|---|---|---|---|---|---|---|---|---|---|
| en.js | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (0 / 0) | 100% | (0 / 0) |
| 1 2 | {}
|